From 45c751c04ed116e7ec6e3777c27624c5ffa3d682 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sun, 30 Jan 2011 08:42:24 +0000 Subject: More demonolithizing of the repl. handlers plus general cruft reduction. It's a comprehensibility party in there. No review. --- .../scala/tools/nsc/InterpreterSettings.scala | 0 .../scala/tools/nsc/interpreter/ILoop.scala | 2 +- .../scala/tools/nsc/interpreter/IMain.scala | 471 ++++++--------------- .../tools/nsc/interpreter/MemberHandlers.scala | 211 +++++++++ .../scala/tools/nsc/interpreter/Naming.scala | 51 +++ .../scala/tools/nsc/interpreter/Power.scala | 2 +- 6 files changed, 389 insertions(+), 348 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/InterpreterSettings.scala create mode 100644 src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala create mode 100644 src/compiler/scala/tools/nsc/interpreter/Naming.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/InterpreterSettings.scala b/src/compiler/scala/tools/nsc/InterpreterSettings.scala deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 03ce7b182b..4b08907dde 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -641,7 +641,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter) (name, objName(value)) } def injectAndName(obj: Any): (String, String) = { - val name = intp.getVarName + val name = intp.naming.freshUserVarName() val className = objName(obj) intp.bind(name, className, obj) (name, className) diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 624ac5c28b..3231aee239 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -19,7 +19,6 @@ import scala.tools.util.PathResolver import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional } import ScalaClassLoader.URLClassLoader import Exceptional.unwrap - import scala.collection.{ mutable, immutable } import scala.PartialFunction.{ cond, condOpt } import scala.util.control.Exception.{ ultimately } @@ -67,7 +66,7 @@ import IMain._ * @author Lex Spoon */ class IMain(val settings: Settings, protected val out: PrintWriter) { - repl => + intp => /** construct an interpreter that reports to Console */ def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true)) @@ -91,7 +90,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { /** reporter */ lazy val reporter: ConsoleReporter = new IMain.ReplReporter(this) - import reporter.{ printMessage => println } + import reporter.{ printMessage, withoutTruncating } // not sure if we have some motivation to print directly to console private def echo(msg: String) { Console println msg } @@ -116,19 +115,19 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { try { new _compiler.Run() compileSources List(new BatchSourceFile("", source)) if (isReplDebug || settings.debug.value) - println("Repl compiler initialized.") + printMessage("Repl compiler initialized.") true } catch { case x: AbstractMethodError => - println(""" + printMessage(""" |Failed to initialize compiler: abstract method error. |This is most often remedied by a full clean and recompile. |""".stripMargin ) x.printStackTrace() false - case x: MissingRequirementError => println(""" + case x: MissingRequirementError => printMessage(""" |Failed to initialize compiler: %s not found. |** Note that as of 2.8 scala does not assume use of the java classpath. |** For the old behavior pass -usejavacp to scala, or if using a Settings @@ -158,10 +157,24 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { import global._ import definitions.{ EmptyPackage, getMember } - import nme.{ - INTERPRETER_VAR_PREFIX, INTERPRETER_SYNTHVAR_PREFIX, INTERPRETER_LINE_PREFIX, - INTERPRETER_IMPORT_WRAPPER, INTERPRETER_WRAPPER_SUFFIX, USCOREkw + import nme.{ INTERPRETER_IMPORT_WRAPPER, INTERPRETER_WRAPPER_SUFFIX } + + object naming extends { + val global: intp.global.type = intp.global + } with Naming { + // make sure we don't overwrite their unwisely named res3 etc. + override def freshUserVarName(): String = { + val name = super.freshUserVarName() + if (definedNameMap contains name) freshUserVarName() + else name + } } + import naming._ + + lazy val memberHandlers = new { + val intp: IMain.this.type = IMain.this + } with MemberHandlers + import memberHandlers._ /** Temporarily be quiet */ def beQuietDuring[T](operation: => T): T = { @@ -255,36 +268,30 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { def setContextClassLoader() = classLoader.setAsContext() /** the previous requests this interpreter has processed */ - private val prevRequests = new mutable.ArrayBuffer[Request]() - private val referencedNameMap = new mutable.HashMap[Name, Request]() - private val boundNameMap = new mutable.HashMap[Name, Request]() + private lazy val prevRequests = mutable.ArrayBuffer[Request]() + private lazy val referencedNameMap = mutable.Map[Name, Request]() + private lazy val definedNameMap: mutable.Map[Name, Request] = mutable.Map[Name, Request]() private def allHandlers = prevRequests.toList flatMap (_.handlers) private def allReqAndHandlers = prevRequests.toList flatMap (req => req.handlers map (req -> _)) - - def pathToTerm(id: String): String = { - val name = id.toTermName - if (boundNameMap contains name) { - val req = boundNameMap(name) - req.fullPath(name) - } - else id - } - - def printAllTypeOf = { - prevRequests foreach { req => - req.typeOf foreach { case (k, v) => echo(k + " => " + v) } - } + private def importHandlers = allHandlers collect { case x: ImportHandler => x } + + def allDefinedNames = definedNameMap.keys.toList sortBy (_.toString) + def pathToType(id: String): String = pathToName(newTypeName(id)) + def pathToTerm(id: String): String = pathToName(newTermName(id)) + def pathToName(name: Name): String = { + if (definedNameMap contains name) + definedNameMap(name) fullPath name + else name.toString } /** Most recent tree handled which wasn't wholly synthetic. */ private def mostRecentlyHandledTree: Option[Tree] = { - for { - req <- prevRequests.reverse - handler <- req.handlers.reverse - name <- handler.generatesValue - if !isSynthVarName(name) - } return Some(handler.member) - + prevRequests.reverse foreach { req => + req.handlers.reverse foreach { + case x: MemberDefHandler if x.definesValue && !isInternalVarName(x.name) => return Some(x.member) + case _ => () + } + } None } @@ -314,12 +321,12 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { prevRequests += req req.referencedNames foreach (x => referencedNameMap(x) = req) - req.boundNames foreach { name => - if (boundNameMap contains name) { - if (name.isTypeName) handleTypeRedefinition(name.toTypeName, boundNameMap(name), req) - else handleTermRedefinition(name.toTermName, boundNameMap(name), req) + req.definedNames foreach { name => + if (definedNameMap contains name) { + if (name.isTypeName) handleTypeRedefinition(name.toTypeName, definedNameMap(name), req) + else handleTermRedefinition(name.toTermName, definedNameMap(name), req) } - boundNameMap(name) = req + definedNameMap(name) = req } // XXX temporarily putting this here because of tricky initialization order issues @@ -328,69 +335,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { quietBind("settings", isettings) } - private def keyList[T](x: collection.Map[T, _]): List[T] = x.keys.toList sortBy (_.toString) - def allreferencedNames = keyList(referencedNameMap) - 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) { - 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 = "" - - def apply(): String = { - x += 1 - val name = pre + x.toString - // make sure we don't overwrite their unwisely named res3 etc. - mostRecent = - if (allBoundNames exists (_.toString == name)) apply() - else name - - mostRecent - } - def reset(): Unit = x = -1 - def didGenerate(name: String) = - (name startsWith pre) && ((name drop pre.length) forall (_.isDigit)) - } - - /** allocate a fresh line name */ - private lazy val lineNameCreator = new NameCreator(INTERPRETER_LINE_PREFIX) - - /** allocate a fresh var name */ - private lazy val varNameCreator = new NameCreator(INTERPRETER_VAR_PREFIX) - - /** allocate a fresh internal variable name */ - private lazy val synthVarNameCreator = new NameCreator(INTERPRETER_SYNTHVAR_PREFIX) - - /** Check if a name looks like it was generated by varNameCreator */ - private def isGeneratedVarName(name: String): Boolean = varNameCreator didGenerate name - private def isSynthVarName(name: String): Boolean = synthVarNameCreator didGenerate name - private def isSynthVarName(name: Name): Boolean = synthVarNameCreator didGenerate name.toString - - def getVarName = varNameCreator() - def getSynthVarName = synthVarNameCreator() - - /** Truncate a string if it is longer than isettings.maxPrintString */ - private def truncPrintString(str: String): String = { - val maxpr = isettings.maxPrintString - val trailer = "..." - - if (maxpr <= 0 || str.length <= maxpr) str - else str.substring(0, maxpr-3) + trailer - } - - /** Clean up a string for output */ - private def clean(str: String) = truncPrintString(cleanNoTruncate(str)) - private def cleanNoTruncate(str: String) = - if (isettings.unwrapStrings) stripWrapperGunk(str) - else str - - implicit def name2string(name: Name) = name.toString + def allSeenTypes = prevRequests.toList flatMap (_.typeOf.values.toList) distinct + def allDefinedTypes = prevRequests.toList flatMap (_.definedTypes.values.toList) distinct + def allImplicits = allHandlers filter (_.definesImplicit) flatMap (_.definedNames) /** Compute imports that allow definitions from previous * requests to be visible in a new request. Returns @@ -433,16 +380,15 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { // try to finesse this, we will mimic all imports for now. def keepHandler(handler: MemberHandler) = handler match { case _: ImportHandler => true - case x => x.definesImplicit || (x.boundNames exists isWanted) + case x => x.definesImplicit || (x.definedNames exists isWanted) } reqs match { case Nil => Nil case rh :: rest if !keepHandler(rh.handler) => select(rest, wanted) case rh :: rest => - val importedNames = rh.handler match { case x: ImportHandler => x.importedNames ; case _ => Nil } import rh.handler._ - val newWanted = wanted ++ referencedNames -- boundNames -- importedNames + val newWanted = wanted ++ referencedNames -- definedNames -- importedNames rh :: select(rest, newWanted) } } @@ -475,19 +421,19 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { if (x.importsWildcard || (currentImps exists (x.importedNames contains _))) addWrapper() - code append (x.member.toString + "\n") + code append (x.member + "\n") // give wildcard imports a import wrapper all to their own if (x.importsWildcard) addWrapper() else currentImps ++= x.importedNames - // For other requests, import each bound variable. + // For other requests, import each defined name. // import them explicitly instead of with _, so that // ambiguity errors will not be generated. Also, quote // the name of the variable, so that we don't need to // handle quoting keywords separately. case x => - for (imv <- x.boundNames) { + for (imv <- x.definedNames) { if (currentImps contains imv) addWrapper() code append ("import %s\n" format (req fullPath imv)) @@ -566,18 +512,6 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { private def buildRequest(line: String, lineName: String, trees: List[Tree]): Request = new Request(line, lineName, trees) - private def chooseHandler(member: Tree): MemberHandler = member match { - case member: DefDef => new DefHandler(member) - case member: ValDef => new ValHandler(member) - case member@Assign(Ident(_), _) => new AssignHandler(member) - case member: ModuleDef => new ModuleHandler(member) - case member: ClassDef => new ClassHandler(member) - case member: TypeDef => new TypeAliasHandler(member) - case member: Import => new ImportHandler(member) - case DocDef(_, documented) => chooseHandler(documented) - case member => new GenericHandler(member) - } - private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = { val trees = parse(indentCode(line)) match { case None => return Left(IR.Incomplete) @@ -586,7 +520,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { } // use synthetic vars to avoid filling up the resXX slots - def varName = if (synthetic) getSynthVarName else getVarName + def varName = if (synthetic) freshInternalVarName() else freshUserVarName() // Treat a single bare expression specially. This is necessary due to it being hard to // modify code at a textual level, and it being hard to submit an AST to the compiler. @@ -601,7 +535,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { } // figure out what kind of request - Right(buildRequest(line, lineNameCreator(), trees)) + Right(buildRequest(line, freshLineName(), trees)) } /**

@@ -622,16 +556,28 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { def interpret(line: String, synthetic: Boolean): IR.Result = { def loadAndRunReq(req: Request) = { val (result, succeeded) = req.loadAndRun - // don't truncate stack traces - if (!succeeded) out print cleanNoTruncate(result) - else if (printResults) out print clean(result) + /** To our displeasure, ConsoleReporter offers only printMessage, + * which tacks a newline on the end. Since that breaks all the + * output checking, we have to take one off to balance. + */ + def show() = { + if (result == "") () + else printMessage(result stripSuffix "\n") + } - // book-keeping - if (succeeded && !synthetic) - recordRequest(req) + if (succeeded) { + if (printResults) + show() + if (!synthetic) // book-keeping + recordRequest(req) - if (succeeded) IR.Success - else IR.Error + IR.Success + } + else { + // don't truncate stack traces + withoutTruncating(show()) + IR.Error + } } if (global == null) IR.Error @@ -646,7 +592,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { } /** A name creator used for objects created by bind(). */ - private lazy val newBinder = new NameCreator("binder") + private lazy val newBinder = new naming.NameCreator("binder") /** Bind a specified name to a specified value. The name may * later be used by expressions passed to interpret. @@ -679,10 +625,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { /** Reset this interpreter, forgetting all user-specified requests. */ def reset() { - virtualDirectory.clear + virtualDirectory.clear() resetClassLoader() - lineNameCreator.reset() - varNameCreator.reset() + resetAllCreators() prevRequests.clear } @@ -693,153 +638,9 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { reporter.flush } - /** A traverser that finds all mentioned identifiers, i.e. things - * that need to be imported. It might return extra names. - */ - private class ImportVarsTraverser extends Traverser { - val importVars = new mutable.HashSet[Name]() - - override def traverse(ast: Tree) = ast match { - case Ident(name) => - // XXX this is obviously inadequate but it's going to require some effort - // to get right. - if (name.toString startsWith "x$") () - else importVars += name - case _ => super.traverse(ast) - } - } - - /** Class to handle one member among all the members included - * in a single interpreter request. - */ - private sealed abstract class MemberHandler(val member: Tree) { - lazy val referencedNames: List[Name] = { - val ivt = new ImportVarsTraverser() - ivt traverse member - ivt.importVars.toList - } - def boundNames: List[Name] = Nil - val definesImplicit = cond(member) { - case tree: MemberDef => tree.mods.isImplicit - } - def generatesValue: Option[Name] = None - - def extraCodeToEvaluate(req: Request): String = "" - def resultExtractionCode(req: Request): String = "" - - override def toString = "%s(used = %s)".format(this.getClass.toString split '.' last, referencedNames) - } - - private class GenericHandler(member: Tree) extends MemberHandler(member) - - private class ValHandler(member: ValDef) extends MemberHandler(member) { - val maxStringElements = 1000 // no need to mkString billions of elements - lazy val ValDef(mods, vname, _, _) = member - lazy val prettyName = NameTransformer.decode(vname) - - override lazy val boundNames = List(vname) - override def generatesValue = Some(vname) - - override def resultExtractionCode(req: Request): String = { - val isInternal = isGeneratedVarName(vname) && req.lookupTypeOf(vname) == "Unit" - if (!mods.isPublic || isInternal) "" - else { - lazy val extractor = "scala.runtime.ScalaRunTime.stringOf(%s, %s)".format(req fullPath vname, maxStringElements) - // if this is a lazy val we avoid evaluating it here - val resultString = if (mods.isLazy) codegenln(false, "") else extractor - - """ + "%s: %s = " + %s""".format(prettyName, string2code(req typeOf vname), resultString) - } - } - } - - private class DefHandler(defDef: DefDef) extends MemberHandler(defDef) { - lazy val DefDef(mods, name, _, vparamss, _, _) = defDef - override lazy val boundNames = List(name) - // true if 0-arity - override def generatesValue = - if (vparamss.isEmpty || vparamss.head.isEmpty) Some(name) - else None - - override def resultExtractionCode(req: Request) = - if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else "" - } - - private class AssignHandler(member: Assign) extends MemberHandler(member) { - val lhs = member.lhs.asInstanceOf[Ident] // an unfortunate limitation - val helperName = newTermName(synthVarNameCreator()) - override def generatesValue = Some(helperName) - - override def extraCodeToEvaluate(req: Request) = - """val %s = %s""".format(helperName, lhs) - - /** Print out lhs instead of the generated varName */ - override def resultExtractionCode(req: Request) = { - val lhsType = string2code(req lookupTypeOf helperName) - val res = string2code(req fullPath helperName) - - """ + "%s: %s = " + %s + "\n" """.format(lhs, lhsType, res) + "\n" - } - } - - private class ModuleHandler(module: ModuleDef) extends MemberHandler(module) { - lazy val ModuleDef(mods, name, _) = module - override lazy val boundNames = List(name) - override def generatesValue = Some(name) - - override def resultExtractionCode(req: Request) = - codegenln("defined module ", name) - } - - private class ClassHandler(classdef: ClassDef) extends MemberHandler(classdef) { - lazy val ClassDef(mods, name, _, _) = classdef - override lazy val boundNames = - name :: (if (mods.isCase) List(name.toTermName) else Nil) - - override def resultExtractionCode(req: Request) = - codegenln("defined %s %s".format(classdef.keyword, name)) - } - - private class TypeAliasHandler(typeDef: TypeDef) extends MemberHandler(typeDef) { - lazy val TypeDef(mods, name, _, _) = typeDef - def isAlias() = mods.isPublic && treeInfo.isAliasTypeDef(typeDef) - override lazy val boundNames = if (isAlias) List(name) else Nil - - override def resultExtractionCode(req: Request) = - codegenln("defined type alias ", name) + "\n" - } - - private class ImportHandler(imp: Import) extends MemberHandler(imp) { - val Import(expr, selectors) = imp - def targetType = stringToCompilerType(expr.toString) match { - case NoType => None - case x => Some(x) - } - - private def selectorWild = selectors filter (_.name == USCOREkw) // wildcard imports, e.g. import foo._ - private def selectorRenames = selectors map (_.rename) filterNot (_ == null) - - /** Whether this import includes a wildcard import */ - val importsWildcard = selectorWild.nonEmpty - - /** Complete list of names imported by a wildcard */ - def wildcardImportedNames: List[Name] = ( - for (tpe <- targetType ; if importsWildcard) yield - tpe.nonPrivateMembers filter (x => x.isMethod && x.isPublic) map (_.name) distinct - ).toList.flatten - - /** The individual names imported by this statement */ - /** XXX come back to this and see what can be done with wildcards now that - * we know how to enumerate the identifiers. - */ - val importedNames: List[Name] = - selectorRenames filterNot (_ == USCOREkw) flatMap (_.bothNames) - - override def resultExtractionCode(req: Request) = codegenln(imp.toString) + "\n" - } - /** One line of code submitted by the user for interpretation */ - private class Request(val line: String, val lineName: String, val trees: List[Tree]) { + // private + class Request(val line: String, val lineName: String, val trees: List[Tree]) { private var _originalLine: String = null def withOriginalLine(s: String): this.type = { _originalLine = s ; this } def originalLine = if (_originalLine == null) line else _originalLine @@ -851,26 +652,17 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { def resultObjectName = RESULT_OBJECT_PREFIX + objectName /** handlers for each tree in this request */ - val handlers: List[MemberHandler] = trees map chooseHandler + val handlers: List[MemberHandler] = trees map (memberHandlers chooseHandler _) /** all (public) names defined by these statements */ - val boundNames = handlers flatMap (_.boundNames) + val definedNames = handlers flatMap (_.definedNames) /** list of names used by this expression */ val referencedNames: List[Name] = handlers flatMap (_.referencedNames) /** def and val names */ - def defNames = partialFlatMap(handlers) { case x: DefHandler => x.boundNames } - def valueNames = partialFlatMap(handlers) { - case x: AssignHandler => List(x.helperName) - 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 - } + def termNames = handlers flatMap (_.definesTerm) + def typeNames = handlers flatMap (_.definesType) /** Code to import bound names from previous lines - accessPath is code to * append to objectName to access anything bound by request. @@ -901,15 +693,17 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { /** We only want to generate this code when the result * is a value which can be referred to as-is. */ - val valueExtractor = handlers.last.generatesValue match { - case Some(vname) if typeOf contains vname => - """ - |lazy val scala_repl_value = { - | scala_repl_result - | %s - |}""".stripMargin.format(fullPath(vname)) - case _ => "" - } + val valueExtractor = + if (!handlers.last.definesValue) "" + else handlers.last.definesTerm match { + case Some(vname) if typeOf contains vname => + """ + |lazy val scala_repl_value = { + | scala_repl_result + | %s + |}""".stripMargin.format(fullPath(vname)) + case _ => "" + } // first line evaluates object to make sure constructor is run // initial "" so later code can uniformly be: + etc val preamble = """ @@ -989,7 +783,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { } /* typeOf lookup with encoding */ - def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name))) + def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name.toString))) def simpleNameOfType(name: TypeName) = (compilerTypeOf get name) map (_.typeSymbol.simpleName) private def typeMap[T](f: Type => T): Map[Name, T] = { @@ -1005,7 +799,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { case tp => f(tp) }) } - valueNames ++ defNames ++ typeNames map (x => x -> toType(x)) toMap + termNames ++ typeNames map (x => x -> toType(x)) toMap } /** Types of variables defined by this request. */ lazy val compilerTypeOf = typeMap[Type](x => x) @@ -1071,15 +865,15 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { */ def mostRecentVar: String = if (mostRecentlyHandledTree.isEmpty) "" - else mostRecentlyHandledTree.get match { + else "" + (mostRecentlyHandledTree.get match { case x: ValOrDefDef => x.name case Assign(Ident(name), _) => name case ModuleDef(_, name, _) => name - case _ => varNameCreator.mostRecent - } + case _ => naming.mostRecentVar + }) private def requestForName(name: Name): Option[Request] = - prevRequests.reverse find (_.boundNames contains name) + prevRequests.reverse find (_.definedNames contains name) private def requestForIdent(line: String): Option[Request] = requestForName(newTermName(line)) @@ -1150,7 +944,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { if (manifest[T] eq Manifest.Nothing) evalError("Could not infer type: try 'eval[SomeType](%s)' instead".format(line)) - val lhs = getSynthVarName + val lhs = freshInternalVarName() beQuietDuring { interpret("val " + lhs + " = { " + line + " } ") } // TODO - can we meaningfully compare the inferred type T with @@ -1179,13 +973,8 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { } /** Another entry point for tab-completion, ids in scope */ - private def unqualifiedIdNames() = partialFlatMap(allHandlers) { - case x: AssignHandler => List(x.helperName) - case x: ValHandler => List(x.vname) - case x: ModuleHandler => List(x.name) - case x: DefHandler => List(x.name) - case x: ImportHandler => x.importedNames - } filterNot isSynthVarName + private def simpleTermNames = + allHandlers flatMap (_.definedOrImported) filter (x => x.isTermName && !isInternalVarName(x)) /** Types which have been wildcard imported, such as: * val x = "abc" ; import x._ // type java.lang.String @@ -1199,12 +988,14 @@ class IMain(val settings: Settings, protected val out: PrintWriter) { * into the compiler scopes. */ def wildcardImportedTypes(): List[Type] = { - val xs = allHandlers collect { case x: ImportHandler if x.importsWildcard => x.targetType } - xs.flatten.reverse.distinct + importHandlers flatMap { + case x if x.importsWildcard => x.targetType + case _ => None + } distinct } /** Another entry point for tab-completion, ids in scope */ - def unqualifiedIds() = (unqualifiedIdNames() map (_.toString)).distinct.sorted + def unqualifiedIds() = (simpleTermNames map (_.toString)).distinct.sorted /** For static/object method completion */ def getClassObject(path: String): Option[Class[_]] = classLoader tryToLoadClass path @@ -1235,14 +1026,26 @@ object IMain { class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, null, intp.out) { import intp._ + /** Truncate a string if it is longer than isettings.maxPrintString */ + private def truncPrintString(str: String): String = { + val maxpr = isettings.maxPrintString + val trailer = "..." + + if (!truncationOK || maxpr <= 0 || str.length <= maxpr) str + else (str take maxpr-3) + trailer + } + + /** Clean up a string for output */ + private def clean(str: String) = truncPrintString( + if (isettings.unwrapStrings) stripWrapperGunk(str) + else str + ) + override def printMessage(msg: String) { if (totalSilence) return - out println ( - if (truncationOK) clean(msg) - else cleanNoTruncate(msg) - ) + out println clean(msg) out.flush() } } @@ -1259,28 +1062,4 @@ object IMain { b.result } - - def codegenln(leadingPlus: Boolean, xs: String*): String = codegen(leadingPlus, (xs ++ Array("\n")): _*) - def codegenln(xs: String*): String = codegenln(true, xs: _*) - - def codegen(xs: String*): String = codegen(true, xs: _*) - def codegen(leadingPlus: Boolean, xs: String*): String = { - val front = if (leadingPlus) "+ " else "" - front + (xs map string2codeQuoted mkString " + ") - } - - def string2codeQuoted(str: String) = "\"" + string2code(str) + "\"" - - /** Convert a string into code that can recreate the string. - * This requires replacing all special characters by escape - * codes. It does not add the surrounding " marks. */ - def string2code(str: String): String = { - val res = new StringBuilder - for (c <- str) c match { - case '"' | '\'' | '\\' => res += '\\' ; res += c - case _ if c.isControl => res ++= Chars.char2uescape(c) - case _ => res += c - } - res.toString - } } diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala new file mode 100644 index 0000000000..29dd33112b --- /dev/null +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -0,0 +1,211 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package interpreter + +import scala.collection.{ mutable, immutable } +import scala.PartialFunction.cond +import scala.reflect.NameTransformer +import util.Chars + +trait MemberHandlers { + val intp: IMain + + import intp.{ Request, global, naming } + import global._ + import naming._ + + def string2codeQuoted(str: String) = "\"" + string2code(str) + "\"" + + /** Convert a string into code that can recreate the string. + * This requires replacing all special characters by escape + * codes. It does not add the surrounding " marks. */ + def string2code(str: String): String = { + val res = new StringBuilder + for (c <- str) c match { + case '"' | '\'' | '\\' => res += '\\' ; res += c + case _ if c.isControl => res ++= Chars.char2uescape(c) + case _ => res += c + } + res.toString + } + def any2stringOf(x: Any, maxlen: Int) = + "scala.runtime.ScalaRunTime.stringOf(%s, %s)".format(x, maxlen) + + private def codegenln(leadingPlus: Boolean, xs: String*): String = codegen(leadingPlus, (xs ++ Array("\n")): _*) + private def codegenln(xs: String*): String = codegenln(true, xs: _*) + + private def codegen(xs: String*): String = codegen(true, xs: _*) + private def codegen(leadingPlus: Boolean, xs: String*): String = { + val front = if (leadingPlus) "+ " else "" + front + (xs map string2codeQuoted mkString " + ") + } + private implicit def name2string(name: Name) = name.toString + + /** A traverser that finds all mentioned identifiers, i.e. things + * that need to be imported. It might return extra names. + */ + private class ImportVarsTraverser extends Traverser { + val importVars = new mutable.HashSet[Name]() + + override def traverse(ast: Tree) = ast match { + case Ident(name) => + // XXX this is obviously inadequate but it's going to require some effort + // to get right. + if (name.toString startsWith "x$") () + else importVars += name + case _ => super.traverse(ast) + } + } + private object ImportVarsTraverser { + def apply(member: Tree) = { + val ivt = new ImportVarsTraverser() + ivt traverse member + ivt.importVars.toList + } + } + + def chooseHandler(member: Tree): MemberHandler = member match { + case member: DefDef => new DefHandler(member) + case member: ValDef => new ValHandler(member) + case member@Assign(Ident(_), _) => new AssignHandler(member) + case member: ModuleDef => new ModuleHandler(member) + case member: ClassDef => new ClassHandler(member) + case member: TypeDef => new TypeAliasHandler(member) + case member: Import => new ImportHandler(member) + case DocDef(_, documented) => chooseHandler(documented) + case member => new GenericHandler(member) + } + + sealed abstract class MemberDefHandler(override val member: MemberDef) extends MemberHandler(member) { + def name: Name = member.name + def mods: Modifiers = member.mods + def keyword = member.keyword + def prettyName = NameTransformer.decode(name) + + override def definesImplicit = member.mods.isImplicit + override def definesTerm: Option[TermName] = Some(name.toTermName) filter (_ => name.isTermName) + override def definesType: Option[TypeName] = Some(name.toTypeName) filter (_ => name.isTypeName) + } + + /** Class to handle one member among all the members included + * in a single interpreter request. + */ + sealed abstract class MemberHandler(val member: Tree) { + def definesImplicit = false + def definesValue = false + + def definesTerm = Option.empty[TermName] + def definesType = Option.empty[TypeName] + + lazy val referencedNames = ImportVarsTraverser(member) + def importedNames = List[Name]() + def definedNames = definesTerm.toList ++ definesType.toList + def definedOrImported = definedNames ++ importedNames + + def extraCodeToEvaluate(req: Request): String = "" + def resultExtractionCode(req: Request): String = "" + + private def shortName = this.getClass.toString split '.' last + override def toString = shortName + referencedNames.mkString(" (refs: ", ", ", ")") + } + + class GenericHandler(member: Tree) extends MemberHandler(member) + + class ValHandler(member: ValDef) extends MemberDefHandler(member) { + val maxStringElements = 1000 // no need to mkString billions of elements + def stringOf(x: Any) = any2stringOf(x, maxStringElements) + override def definesValue = true + + override def resultExtractionCode(req: Request): String = { + val isInternal = isUserVarName(name) && req.lookupTypeOf(name) == "Unit" + if (!mods.isPublic || isInternal) "" + else { + // if this is a lazy val we avoid evaluating it here + val resultString = + if (mods.isLazy) codegenln(false, "") + else stringOf(req fullPath name) + + """ + "%s: %s = " + %s""".format(prettyName, string2code(req typeOf name), resultString) + } + } + } + + class DefHandler(member: DefDef) extends MemberDefHandler(member) { + private def vparamss = member.vparamss + // true if 0-arity + override def definesValue = vparamss.isEmpty || vparamss.head.isEmpty + override def resultExtractionCode(req: Request) = + if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else "" + } + + class AssignHandler(member: Assign) extends MemberHandler(member) { + val lhs = member.lhs.asInstanceOf[Ident] // an unfortunate limitation + val name = newTermName(freshInternalVarName()) + + override def definesTerm = Some(name) + override def definesValue = true + override def extraCodeToEvaluate(req: Request) = + """val %s = %s""".format(name, lhs) + + /** Print out lhs instead of the generated varName */ + override def resultExtractionCode(req: Request) = { + val lhsType = string2code(req lookupTypeOf name) + val res = string2code(req fullPath name) + + """ + "%s: %s = " + %s + "\n" """.format(lhs, lhsType, res) + "\n" + } + } + + class ModuleHandler(module: ModuleDef) extends MemberDefHandler(module) { + override def definesTerm = Some(name) + override def definesValue = true + + override def resultExtractionCode(req: Request) = codegenln("defined module ", name) + } + + class ClassHandler(member: ClassDef) extends MemberDefHandler(member) { + override def definesType = Some(name.toTypeName) + override def definesTerm = Some(name.toTermName) filter (_ => mods.isCase) + + override def resultExtractionCode(req: Request) = + codegenln("defined %s %s".format(keyword, name)) + } + + class TypeAliasHandler(member: TypeDef) extends MemberDefHandler(member) { + private def isAlias = mods.isPublic && treeInfo.isAliasTypeDef(member) + override def definesType = Some(name.toTypeName) filter (_ => isAlias) + + override def resultExtractionCode(req: Request) = + codegenln("defined type alias ", name) + "\n" + } + + class ImportHandler(imp: Import) extends MemberHandler(imp) { + val Import(expr, selectors) = imp + def targetType = Some(intp.stringToCompilerType(expr.toString)) filterNot (_ == NoType) + + private def selectorWild = selectors filter (_.name == nme.USCOREkw) // wildcard imports, e.g. import foo._ + private def selectorRenames = selectors map (_.rename) filterNot (_ == null) + + /** Whether this import includes a wildcard import */ + val importsWildcard = selectorWild.nonEmpty + + /** Complete list of names imported by a wildcard */ + def wildcardImportedNames: List[Name] = ( + for (tpe <- targetType ; if importsWildcard) yield + tpe.nonPrivateMembers filter (x => x.isMethod && x.isPublic) map (_.name) distinct + ).toList.flatten + + /** The individual names imported by this statement */ + /** XXX come back to this and see what can be done with wildcards now that + * we know how to enumerate the identifiers. + */ + override lazy val importedNames: List[Name] = + selectorRenames filterNot (_ == nme.USCOREkw) flatMap (_.bothNames) + + override def resultExtractionCode(req: Request) = codegenln(imp.toString) + "\n" + } +} diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala new file mode 100644 index 0000000000..396f2deeba --- /dev/null +++ b/src/compiler/scala/tools/nsc/interpreter/Naming.scala @@ -0,0 +1,51 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.nsc +package interpreter + +trait Naming { + val global: Global + + import global.{ Name, nme } + import nme.{ + INTERPRETER_VAR_PREFIX, INTERPRETER_SYNTHVAR_PREFIX, INTERPRETER_LINE_PREFIX + } + + /** Generates names pre0, pre1, etc. via calls to apply method */ + class NameCreator(pre: String) { + private var x = -1 + var mostRecent: String = "" + + def apply(): String = { + x += 1 + mostRecent = pre + x.toString + mostRecent + } + def reset(): Unit = x = -1 + def didGenerate(name: String) = + (name startsWith pre) && ((name drop pre.length) forall (_.isDigit)) + } + + private lazy val line = new NameCreator(INTERPRETER_LINE_PREFIX) // line name, like line$0 + private lazy val userVar = new NameCreator(INTERPRETER_VAR_PREFIX) // var name, like res0 + private lazy val internalVar = new NameCreator(INTERPRETER_SYNTHVAR_PREFIX) // internal var name, like $synthvar0 + + def isUserVarName(name: String) = userVar didGenerate name + def isInternalVarName(name: String): Boolean = internalVar didGenerate name + def isInternalVarName(name: Name): Boolean = internalVar didGenerate name.toString + + def freshLineName() = line() + def freshUserVarName() = userVar() + def freshInternalVarName() = internalVar() + + def resetAllCreators() { + line.reset() + userVar.reset() + internalVar.reset() + } + + def mostRecentVar = userVar.mostRecent +} diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index 6bb70ea02e..e611c5f0b6 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -140,7 +140,7 @@ class Power(repl: ILoop, intp: IMain) { |Identifiers: %s """.stripMargin.format( phased.get, - intp.allreferencedNames mkString " ", + intp.allDefinedNames mkString " ", intp.unqualifiedIds mkString " " ) } -- cgit v1.2.3