diff options
author | Paul Phillips <paulp@improving.org> | 2010-11-27 22:53:42 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-11-27 22:53:42 +0000 |
commit | a5553b8384a6e4cb6071846368bb0d17be2e246f (patch) | |
tree | 76299ca2948389e4a05d28b8a6e4ede6c3157d50 /src | |
parent | 3e3e3564ca2771b0946c9c8b35a6a393f4330354 (diff) | |
download | scala-a5553b8384a6e4cb6071846368bb0d17be2e246f.tar.gz scala-a5553b8384a6e4cb6071846368bb0d17be2e246f.tar.bz2 scala-a5553b8384a6e4cb6071846368bb0d17be2e246f.zip |
Some hardening of the repl's internals extracte...
Some hardening of the repl's internals extracted from a more interesting
patch in progress. No review.
Diffstat (limited to 'src')
6 files changed, 98 insertions, 58 deletions
diff --git a/src/compiler/scala/tools/cmd/program/Tokens.scala b/src/compiler/scala/tools/cmd/program/Tokens.scala index 36786aa2b7..9be1eb4325 100644 --- a/src/compiler/scala/tools/cmd/program/Tokens.scala +++ b/src/compiler/scala/tools/cmd/program/Tokens.scala @@ -70,6 +70,12 @@ object Tokens { Path onlyFiles traverse filter (_ hasExtension "scala") toList } + def fromScalaString(code: String): List[Any] = { + val f = File.makeTemp("tokens") + f writeAll code + fromScalaSource(f) + } + /** Tokenizes a single scala file. */ def fromScalaSource(file: Path): List[Any] = fromScalaSource(file.path) diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index 8a22c76ab5..e9474e973c 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -8,29 +8,28 @@ package scala.tools.nsc import Predef.{ println => _, _ } import java.io.{ File, PrintWriter, StringWriter, Writer } import File.pathSeparator -import java.lang.{ Class, ClassLoader } +import java.lang.{ reflect, Class, ClassLoader => JavaClassLoader } import java.net.{ MalformedURLException, URL } -import java.lang.reflect -import java.util.concurrent.Future import reflect.InvocationTargetException +import java.util.concurrent.Future -import scala.collection.{ mutable, immutable } -import scala.PartialFunction.{ cond, condOpt } +import util.{ Set => _, _ } +import interpreter._ +import io.{ PlainFile, VirtualDirectory, spawn, callable, newDaemonThreadExecutor } +import reporters.{ ConsoleReporter, Reporter } +import symtab.{ Flags, Names } +import scala.tools.nsc.{ InterpreterResults => IR } import scala.tools.util.{ PathResolver, SignalManager } -import scala.reflect.Manifest -import scala.collection.mutable.{ ListBuffer, HashSet, HashMap, ArrayBuffer } import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional } import ScalaClassLoader.URLClassLoader import Exceptional.unwrap -import scala.util.control.Exception.{ Catcher, catching, catchingPromiscuously, ultimately } -import io.{ PlainFile, VirtualDirectory, spawn, callable, newDaemonThreadExecutor } -import reporters.{ ConsoleReporter, Reporter } -import symtab.{ Flags, Names } -import util.{ ScalaPrefs, JavaStackFrame, SourceFile, BatchSourceFile, ScriptSourceFile, ClassPath, Chars, stringFromWriter } +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.{ ListBuffer, ArrayBuffer } +import scala.PartialFunction.{ cond, condOpt } +import scala.util.control.Exception.{ Catcher, catching, catchingPromiscuously, ultimately, unwrapping } import scala.reflect.NameTransformer -import scala.tools.nsc.{ InterpreterResults => IR } -import interpreter._ + import Interpreter._ /** <p> @@ -76,6 +75,8 @@ import Interpreter._ class Interpreter(val settings: Settings, out: PrintWriter) { repl => + private val RESULT_OBJECT_PREFIX = "RequestResult$" + def println(x: Any) = { out.println(x) out.flush() @@ -252,10 +253,10 @@ class Interpreter(val settings: Settings, out: PrintWriter) { def setContextClassLoader() = classLoader.setAsContext() /** the previous requests this interpreter has processed */ - private val prevRequests = new ArrayBuffer[Request]() - private val usedNameMap = new HashMap[Name, Request]() - private val boundNameMap = new HashMap[Name, Request]() - private def allHandlers = prevRequests.toList flatMap (_.handlers) + private val prevRequests = new ArrayBuffer[Request]() + private val usedNameMap = new mutable.HashMap[Name, Request]() + private val boundNameMap = new mutable.HashMap[Name, Request]() + private def allHandlers = prevRequests.toList flatMap (_.handlers) private def allReqAndHandlers = prevRequests.toList flatMap (req => req.handlers map (req -> _)) def printAllTypeOf = { @@ -276,6 +277,15 @@ class Interpreter(val settings: Settings, out: PrintWriter) { None } + /** Stubs for work in progress. */ + def handleTypeRedefinition(name: Name, old: Request, req: Request) = { + DBG("Redefining type '%s'\n %s -> %s".format(name, old simpleNameOfType name, req simpleNameOfType name)) + } + + def handleTermRedefinition(name: Name, old: Request, req: Request) = { + DBG("Redefining term '%s'\n %s -> %s".format(name, old compilerTypeOf name, req compilerTypeOf name)) + } + def recordRequest(req: Request) { def tripart[T](set1: Set[T], set2: Set[T]) = { val intersect = set1 intersect set2 @@ -284,7 +294,14 @@ class Interpreter(val settings: Settings, out: PrintWriter) { prevRequests += req req.usedNames foreach (x => usedNameMap(x) = req) - req.boundNames foreach (x => boundNameMap(x) = req) + + req.boundNames foreach { name => + if (boundNameMap contains name) { + if (name.isTypeName) handleTypeRedefinition(name, boundNameMap(name), req) + else handleTermRedefinition(name, boundNameMap(name), req) + } + boundNameMap(name) = req + } // XXX temporarily putting this here because of tricky initialization order issues // so right now it's not bound until after you issue a command. @@ -297,18 +314,19 @@ class Interpreter(val settings: Settings, out: PrintWriter) { } private def keyList[T](x: collection.Map[T, _]): List[T] = x.keys.toList sortBy (_.toString) - def allUsedNames = keyList(usedNameMap) - def allBoundNames = keyList(boundNameMap) - def allSeenTypes = prevRequests.toList flatMap (_.typeOf.values.toList) distinct + def allUsedNames = keyList(usedNameMap) + def allBoundNames = keyList(boundNameMap) + def allSeenTypes = prevRequests.toList flatMap (_.typeOf.values.toList) distinct + def allDefinedTypes = prevRequests.toList flatMap (_.definedTypes.values.toList) distinct def allValueGeneratingNames = allHandlers flatMap (_.generatesValue) - def allImplicits = partialFlatMap(allHandlers) { + def allImplicits = partialFlatMap(allHandlers) { case x: MemberHandler if x.definesImplicit => x.boundNames } /** Generates names pre0, pre1, etc. via calls to apply method */ class NameCreator(pre: String) { private var x = -1 - var mostRecent: String = null + var mostRecent: String = "" def apply(): String = { x += 1 @@ -437,7 +455,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { } val code, trailingBraces, accessPath = new StringBuffer - val currentImps = HashSet[Name]() + val currentImps = mutable.HashSet[Name]() // add code for a new object to hold some imports def addWrapper() { @@ -522,7 +540,11 @@ class Interpreter(val settings: Settings, out: PrintWriter) { compileSources(new BatchSourceFile("<script>", code)) def compileAndSaveRun(label: String, code: String) = { - if (isReplDebug) { + /** Secret bookcase entrance for repl debuggers: end the line + * with "// show" and see what's going on. + */ + if (code.lines exists (_.trim endsWith "// show")) { + Console println code parse(code) match { case Some(trees) => trees foreach (t => DBG(compiler.asCompactString(t))) case _ => DBG("Parse error:\n\n" + code) @@ -670,7 +692,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { * that need to be imported. It might return extra names. */ private class ImportVarsTraverser extends Traverser { - val importVars = new HashSet[Name]() + val importVars = new mutable.HashSet[Name]() override def traverse(ast: Tree) = ast match { // XXX this is obviously inadequate but it's going to require some effort @@ -712,7 +734,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { override def generatesValue = Some(vname) override def resultExtractionCode(req: Request, code: PrintWriter) { - val isInternal = isGeneratedVarName(vname) && req.typeOfEnc(vname) == "Unit" + val isInternal = isGeneratedVarName(vname) && req.lookupTypeOf(vname) == "Unit" if (!mods.isPublic || isInternal) return lazy val extractor = "scala.runtime.ScalaRunTime.stringOf(%s, %s)".format(req fullPath vname, maxStringElements) @@ -748,7 +770,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { /** Print out lhs instead of the generated varName */ override def resultExtractionCode(req: Request, code: PrintWriter) { - val lhsType = string2code(req typeOfEnc helperName) + val lhsType = string2code(req lookupTypeOf helperName) val res = string2code(req fullPath helperName) val codeToPrint = """ + "%s: %s = " + %s + "\n" """.format(lhs, lhsType, res) @@ -819,7 +841,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { def objectName = lineName + INTERPRETER_WRAPPER_SUFFIX /** name of the object that retrieves the result from the above object */ - def resultObjectName = "RequestResult$" + objectName + def resultObjectName = RESULT_OBJECT_PREFIX + objectName /** handlers for each tree in this request */ val handlers: List[MemberHandler] = trees map chooseHandler @@ -837,6 +859,11 @@ class Interpreter(val settings: Settings, out: PrintWriter) { case x: ValHandler => boundNames case x: ModuleHandler => List(x.name) } + /** Type names */ + def typeNames = handlers collect { + case x: ClassHandler => x.name + case x: TypeAliasHandler => x.name + } /** Code to import bound names from previous lines - accessPath is code to * append to objectName to access anything bound by request. @@ -935,6 +962,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { // extract and remember types typeOf + definedTypes // compile the result-extraction object extractionObjectRun @@ -943,7 +971,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { !reporter.hasErrors } - def atNextPhase[T](op: => T): T = compiler.atPhase(objRun.typerPhase.next)(op) + def afterTyper[T](op: => T): T = compiler.atPhase(objRun.typerPhase.next)(op) /** The outermost wrapper object */ lazy val outerResObjSym: Symbol = getMember(EmptyPackage, newTermName(objectName)) @@ -953,34 +981,38 @@ class Interpreter(val settings: Settings, out: PrintWriter) { lazy val resObjSym = accessPath.split("\\.").foldLeft(outerResObjSym) { (sym, name) => if (name == "") sym else - atNextPhase(sym.info member newTermName(name)) + afterTyper(sym.info member newTermName(name)) } /* typeOf lookup with encoding */ - def typeOfEnc(vname: Name) = typeOf(compiler encode vname) - - /** Types of variables defined by this request. */ - lazy val typeOf: Map[Name, String] = { - def getTypes(names: List[Name], nameMap: Name => Name): Map[Name, String] = { - names.foldLeft(Map.empty[Name, String]) { (map, name) => - val tp1 = atNextPhase(resObjSym.info.nonPrivateDecl(name).tpe) - // the types are all =>T; remove the => - val tp2 = tp1 match { - case PolyType(Nil, tp) => tp - case tp => tp - } - // normalize non-public types so we don't see protected aliases like Self - val tp3 = compiler.atPhase(objRun.typerPhase)(tp2 match { - case TypeRef(_, sym, _) if !sym.isPublic => tp2.normalize.toString - case tp => tp.toString - }) - - map + (name -> tp3) - } + def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(compiler encode name)) + + def simpleNameOfType(name: Name) = compilerTypeOf(name).typeSymbol.simpleName + + private def typeMap[T](f: Type => T): Map[Name, T] = { + def toType(name: Name): T = { + // the types are all =>T; remove the => + val tp1 = afterTyper(resObjSym.info.nonPrivateDecl(name).tpe match { + case PolyType(Nil, tp) => tp + case tp => tp + }) + // normalize non-public types so we don't see protected aliases like Self + afterTyper(tp1 match { + case TypeRef(_, sym, _) if !sym.isPublic => f(tp1.normalize) + case tp => f(tp) + }) } + valueNames ++ defNames ++ typeNames map (x => x -> toType(x)) toMap + } + /** Types of variables defined by this request. */ + lazy val compilerTypeOf = typeMap[Type](x => x) + /** String representations of same. */ + lazy val typeOf = typeMap[String](_.toString) - getTypes(valueNames, nme.getterToLocal(_)) ++ getTypes(defNames, identity) + lazy val definedTypes: Map[Name, Type] = { + typeNames map (x => x -> afterTyper(resObjSym.info.nonPrivateDecl(x).tpe)) toMap } + private def bindExceptionally(t: Throwable) = { val ex: Exceptional = if (isettings.showInternalStackTraces) Exceptional(t) @@ -1095,7 +1127,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { case x: ValOrDefDef => x.name case Assign(Ident(name), _) => name case ModuleDef(_, name, _) => name - case _ => onull(varNameCreator.mostRecent) + case _ => varNameCreator.mostRecent } private def requestForName(name: Name): Option[Request] = @@ -1103,6 +1135,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { private def requestForIdent(line: String): Option[Request] = requestForName(newTermName(line)) + // XXX literals. def stringToCompilerType(id: String): compiler.Type = { // if it's a recognized identifier, the type of that; otherwise treat the // String like a value (e.g. scala.collection.Map) . diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala index 22a95a4bf8..426f2debf4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala @@ -310,7 +310,7 @@ class Completion(val repl: Interpreter) extends CompletionOutput { // This is jline's entry point for completion. override def complete(_buf: String, cursor: Int, candidates: JList[String]): Int = { - val buf = onull(_buf) + val buf = if (_buf == null) "" else _buf verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0 DBG("\ncomplete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, verbosity)) diff --git a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala index 84f5477c21..d5953d3a23 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala @@ -22,6 +22,7 @@ class Parsed private ( def isAtStart = cursor <= 0 private var _verbosity = 0 + def verbosity = _verbosity def withVerbosity(v: Int): this.type = returning[this.type](this)(_ => _verbosity = v) @@ -56,6 +57,8 @@ class Parsed private ( } object Parsed { + private def onull(s: String) = if (s == null) "" else s + def apply(s: String): Parsed = apply(onull(s), onull(s).length) def apply(s: String, cursor: Int): Parsed = apply(onull(s), cursor, "{},`; \t" contains _) def apply(s: String, cursor: Int, delimited: Char => Boolean): Parsed = diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala index eaf736c5b7..c57874e59a 100644 --- a/src/compiler/scala/tools/nsc/interpreter/package.scala +++ b/src/compiler/scala/tools/nsc/interpreter/package.scala @@ -12,9 +12,6 @@ package object interpreter { /** Frequency counter */ def freq[T](seq: Seq[T]) = seq groupBy identity mapValues (_.length) - /** null becomes "", otherwise identity */ - def onull(s: String) = if (s == null) "" else s - /** Heuristically strip interpreter wrapper prefixes * from an interpreter output string. */ diff --git a/src/compiler/scala/tools/util/StringOps.scala b/src/compiler/scala/tools/util/StringOps.scala index a3e7c512eb..216e9f1f6c 100644 --- a/src/compiler/scala/tools/util/StringOps.scala +++ b/src/compiler/scala/tools/util/StringOps.scala @@ -17,6 +17,7 @@ package util * @version 1.0 */ object StringOps { + def onull(s: String) = if (s == null) "" else s def oempty(xs: String*) = xs filterNot (x => x == null || x == "") def ojoin(xs: Seq[String], sep: String) = oempty(xs: _*) mkString sep def ojoinOr(xs: Seq[String], sep: String, orElse: String) = { |