diff options
Diffstat (limited to 'src/repl/scala/tools/nsc')
21 files changed, 205 insertions, 1106 deletions
diff --git a/src/repl/scala/tools/nsc/MainGenericRunner.scala b/src/repl/scala/tools/nsc/MainGenericRunner.scala index 34057ed341..747b684293 100644 --- a/src/repl/scala/tools/nsc/MainGenericRunner.scala +++ b/src/repl/scala/tools/nsc/MainGenericRunner.scala @@ -6,8 +6,8 @@ package scala package tools.nsc -import io.{ File } -import util.{ ClassPath, ScalaClassLoader } +import io.File +import util.ClassPath import GenericRunnerCommand._ object JarRunner extends CommonRunner { diff --git a/src/repl/scala/tools/nsc/interpreter/Completion.scala b/src/repl/scala/tools/nsc/interpreter/Completion.scala index 9ad7f95fae..6f5194d2f9 100644 --- a/src/repl/scala/tools/nsc/interpreter/Completion.scala +++ b/src/repl/scala/tools/nsc/interpreter/Completion.scala @@ -13,35 +13,22 @@ import Completion._ */ trait Completion { def resetVerbosity(): Unit - def completer(): ScalaCompleter + def complete(buffer: String, cursor: Int): Candidates } object NoCompletion extends Completion { def resetVerbosity() = () - def completer() = NullCompleter + def complete(buffer: String, cursor: Int) = NoCandidates } object Completion { case class Candidates(cursor: Int, candidates: List[String]) { } val NoCandidates = Candidates(-1, Nil) - object NullCompleter extends ScalaCompleter { - def complete(buffer: String, cursor: Int): Candidates = NoCandidates - } - trait ScalaCompleter { - def complete(buffer: String, cursor: Int): Candidates - } - - def looksLikeInvocation(code: String) = ( - (code != null) - && (code startsWith ".") - && !(code == ".") - && !(code startsWith "./") - && !(code startsWith "..") - ) - object Forwarder { - def apply(forwardTo: () => Option[CompletionAware]): CompletionAware = new CompletionAware { - def completions(verbosity: Int) = forwardTo() map (_ completions verbosity) getOrElse Nil - override def follow(s: String) = forwardTo() flatMap (_ follow s) - } + // a leading dot plus something, but not ".." or "./", ignoring leading whitespace + private val dotlike = """\s*\.[^./].*""".r + def looksLikeInvocation(code: String) = code match { + case null => false // insurance + case dotlike() => true + case _ => false } } diff --git a/src/repl/scala/tools/nsc/interpreter/CompletionAware.scala b/src/repl/scala/tools/nsc/interpreter/CompletionAware.scala deleted file mode 100644 index 3dd5d93390..0000000000 --- a/src/repl/scala/tools/nsc/interpreter/CompletionAware.scala +++ /dev/null @@ -1,53 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -/** An interface for objects which are aware of tab completion and - * will supply their own candidates and resolve their own paths. - */ -trait CompletionAware { - /** The complete list of unqualified Strings to which this - * object will complete. - */ - def completions(verbosity: Int): List[String] - - /** The next completor in the chain. - */ - def follow(id: String): Option[CompletionAware] = None - - /** A list of useful information regarding a specific uniquely - * identified completion. This is specifically written for the - * following situation, but should be useful elsewhere too: - * - * x.y.z.methodName<tab> - * - * If "methodName" is among z's completions, and verbosity > 0 - * indicating tab has been pressed twice consecutively, then we - * call alternativesFor and show a list of overloaded method - * signatures. - */ - def alternativesFor(id: String): List[String] = Nil - - /** Given string 'buf', return a list of all the strings - * to which it can complete. This may involve delegating - * to other CompletionAware objects. - */ - def completionsFor(parsed: Parsed): List[String] = { - import parsed.{ buffer, verbosity } - val comps = completions(verbosity) filter (_ startsWith buffer) - val exact = comps contains buffer - - val results = - if (parsed.isEmpty) comps - else if (parsed.isUnqualified && !parsed.isLastDelimiter) - if (verbosity > 0 && exact) alternativesFor(buffer) - else comps - else follow(parsed.bufferHead) map (_ completionsFor parsed.bufferTail) getOrElse Nil - - results.sorted - } -} diff --git a/src/repl/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/repl/scala/tools/nsc/interpreter/CompletionOutput.scala deleted file mode 100644 index d24ad60974..0000000000 --- a/src/repl/scala/tools/nsc/interpreter/CompletionOutput.scala +++ /dev/null @@ -1,85 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -/** This has a lot of duplication with other methods in Symbols and Types, - * but repl completion utility is very sensitive to precise output. Best - * thing would be to abstract an interface for how such things are printed, - * as is also in progress with error messages. - */ -trait CompletionOutput { - val global: Global - - import global._ - import definitions.{ isTupleType, isFunctionType, isRepeatedParamType } - - /** Reducing fully qualified noise for some common packages. - */ - val typeTransforms = List( - "java.lang." -> "", - "scala.collection.immutable." -> "immutable.", - "scala.collection.mutable." -> "mutable.", - "scala.collection.generic." -> "generic." - ) - - def quietString(tp: String): String = - typeTransforms.foldLeft(tp) { - case (str, (prefix, replacement)) => - if (str startsWith prefix) replacement + (str stripPrefix prefix) - else str - } - - class MethodSymbolOutput(method: Symbol) { - val pkg = method.ownerChain find (_.isPackageClass) map (_.fullName) getOrElse "" - - def relativize(str: String): String = quietString(str stripPrefix (pkg + ".")) - def relativize(tp: Type): String = relativize(tp.dealiasWiden.toString) - - def braceList(tparams: List[String]) = if (tparams.isEmpty) "" else (tparams map relativize).mkString("[", ", ", "]") - def parenList(params: List[Any]) = params.mkString("(", ", ", ")") - - def methodTypeToString(mt: MethodType) = - (mt.paramss map paramsString mkString "") + ": " + relativize(mt.finalResultType) - - def typeToString(tp: Type): String = relativize( - tp match { - case x if isFunctionType(x) => functionString(x) - case x if isTupleType(x) => tupleString(x) - case x if isRepeatedParamType(x) => typeToString(x.typeArgs.head) + "*" - case mt @ MethodType(_, _) => methodTypeToString(mt) - case x => x.toString - } - ) - - def tupleString(tp: Type) = parenList(tp.dealiasWiden.typeArgs map relativize) - def functionString(tp: Type) = tp.dealiasWiden.typeArgs match { - case List(t, r) => t + " => " + r - case xs => parenList(xs.init) + " => " + xs.last - } - - def tparamsString(tparams: List[Symbol]) = braceList(tparams map (_.defString)) - def paramsString(params: List[Symbol]) = { - def paramNameString(sym: Symbol) = if (sym.isSynthetic) "" else sym.nameString + ": " - def paramString(sym: Symbol) = paramNameString(sym) + typeToString(sym.info.dealiasWiden) - - val isImplicit = params.nonEmpty && params.head.isImplicit - val strs = (params map paramString) match { - case x :: xs if isImplicit => ("implicit " + x) :: xs - case xs => xs - } - parenList(strs) - } - - def methodString() = - method.keyString + " " + method.nameString + (method.info.dealiasWiden match { - case NullaryMethodType(resType) => ": " + typeToString(resType) - case PolyType(tparams, resType) => tparamsString(tparams) + typeToString(resType) - case mt @ MethodType(_, _) => methodTypeToString(mt) - case x => x.toString - }) - } -} diff --git a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala index 8a6a405810..01e3a90950 100644 --- a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala @@ -6,8 +6,6 @@ package scala.tools.nsc package interpreter -import scala.tools.nsc.ast.parser.Tokens.EOF - trait ExprTyper { val repl: IMain diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index adac438b37..56d62f3efc 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -12,18 +12,17 @@ import Predef.{ println => _, _ } import interpreter.session._ import StdReplTags._ import scala.tools.asm.ClassReader -import scala.util.Properties.{ jdkHome, javaVersion, versionString, javaVmName } -import scala.tools.nsc.util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream } +import scala.util.Properties.jdkHome +import scala.tools.nsc.util.{ ClassPath, stringFromStream } import scala.reflect.classTag import scala.reflect.internal.util.{ BatchSourceFile, ScalaClassLoader } import ScalaClassLoader._ -import scala.reflect.io.{ File, Directory } +import scala.reflect.io.File import scala.tools.util._ import io.AbstractFile -import scala.collection.generic.Clearable -import scala.concurrent.{ ExecutionContext, Await, Future, future } +import scala.concurrent.{ ExecutionContext, Await, Future } import ExecutionContext.Implicits._ -import java.io.{ BufferedReader, FileReader, StringReader } +import java.io.BufferedReader import scala.util.{ Try, Success, Failure } @@ -266,8 +265,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - protected def newJavap() = - JavapClass(addToolsJarToLoader(), new IMain.ReplStrippingWriter(intp), Some(intp)) + protected def newJavap() = JavapClass(addToolsJarToLoader(), new IMain.ReplStrippingWriter(intp), intp) private lazy val javap = substituteAndLog[Javap]("javap", NoJavap)(newJavap()) @@ -306,7 +304,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) if (javap == null) s":javap unavailable, no tools.jar at $jdkHome. Set JDK_HOME." else if (line == "") - ":javap [-lcsvp] [path1 path2 ...]" + Javap.helpText else javap(words(line)) foreach { res => if (res.isError) return s"Failed: ${res.value}" @@ -544,7 +542,6 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) case i => val n = s.take(i).toInt ; (n, s.drop(i+1).toInt - n) } } - import scala.collection.JavaConverters._ val index = (start - 1) max 0 val text = history.asStrings(index, index + len) mkString "\n" edit(text) @@ -636,10 +633,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } def alreadyDefined(clsName: String) = intp.classLoader.tryToLoadClass(clsName).isDefined - val exists = entries.filter(_.hasExtension("class")).map(classNameOf).exists(alreadyDefined) + val existingClass = entries.filter(_.hasExtension("class")).map(classNameOf).find(alreadyDefined) if (!f.exists) echo(s"The path '$f' doesn't seem to exist.") - else if (exists) echo(s"The path '$f' cannot be loaded, because existing classpath entries conflict.") // TODO tell me which one + else if (existingClass.nonEmpty) echo(s"The path '$f' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") else { addedClasspath = ClassPath.join(addedClasspath, f.path) intp.addUrlsToClassPath(f.toURI.toURL) @@ -860,12 +857,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } def mkReader(maker: ReaderMaker) = maker { () => - settings.completion.value match { - case _ if settings.noCompletion => NoCompletion - case "none" => NoCompletion - case "adhoc" => new JLineCompletion(intp) // JLineCompletion is a misnomer; it's not tied to jline - case "pc" | _ => new PresentationCompilerCompleter(intp) - } + if (settings.noCompletion) NoCompletion else new PresentationCompilerCompleter(intp) } def internalClass(kind: String) = s"scala.tools.nsc.interpreter.$kind.InteractiveReader" @@ -912,7 +904,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) // sets in to some kind of reader depending on environmental cues in = in0.fold(chooseReader(settings))(r => SimpleReader(r, out, interactive = true)) - globalFuture = future { + globalFuture = Future { intp.initializeSynchronous() loopPostInit() !intp.reporter.hasErrors diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index ef6ab4063a..8c91242b36 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -11,23 +11,18 @@ import PartialFunction.cond import scala.language.implicitConversions import scala.beans.BeanProperty import scala.collection.mutable -import scala.concurrent.{ Future, ExecutionContext } -import scala.reflect.runtime.{ universe => ru } -import scala.reflect.{ ClassTag, classTag } -import scala.reflect.internal.util.{ BatchSourceFile, SourceFile } -import scala.tools.nsc.interactive -import scala.tools.nsc.reporters.StoreReporter -import scala.tools.nsc.util.ClassPath.DefaultJavaContext -import scala.tools.util.PathResolverFactory +import scala.concurrent.{ExecutionContext, Future} +import scala.reflect.runtime.{universe => ru} +import scala.reflect.{ClassTag, classTag} +import scala.reflect.internal.util.{BatchSourceFile, SourceFile} import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.typechecker.{ TypeStrings, StructuredTypeStrings } +import scala.tools.nsc.typechecker.{StructuredTypeStrings, TypeStrings} import scala.tools.nsc.util._ import ScalaClassLoader.URLClassLoader import scala.tools.nsc.util.Exceptional.unwrap -import scala.tools.nsc.backend.JavaPlatform -import javax.script.{AbstractScriptEngine, Bindings, ScriptContext, ScriptEngine, ScriptEngineFactory, ScriptException, CompiledScript, Compilable} +import javax.script.{AbstractScriptEngine, Bindings, Compilable, CompiledScript, ScriptContext, ScriptEngine, ScriptEngineFactory, ScriptException} import java.net.URL -import java.io.File +import scala.tools.util.PathResolver /** An interpreter for Scala code. * @@ -96,7 +91,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def compilerClasspath: Seq[java.net.URL] = ( if (isInitializeComplete) global.classPath.asURLs - else PathResolverFactory.create(settings).resultAsURLs // the compiler's classpath + else new PathResolver(settings).resultAsURLs // the compiler's classpath ) def settings = initialSettings // Run the code body with the given boolean settings flipped to true. diff --git a/src/repl/scala/tools/nsc/interpreter/Imports.scala b/src/repl/scala/tools/nsc/interpreter/Imports.scala index 71a5e9f00a..0cda9c4da3 100644 --- a/src/repl/scala/tools/nsc/interpreter/Imports.scala +++ b/src/repl/scala/tools/nsc/interpreter/Imports.scala @@ -6,13 +6,13 @@ package scala.tools.nsc package interpreter -import scala.collection.{ mutable, immutable } +import scala.collection.mutable trait Imports { self: IMain => import global._ - import definitions.{ ObjectClass, ScalaPackage, JavaLangPackage, PredefModule } + import definitions.{ ScalaPackage, JavaLangPackage, PredefModule } import memberHandlers._ /** Synthetic import handlers for the language defined imports. */ diff --git a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala deleted file mode 100644 index c2ccfc8064..0000000000 --- a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala +++ /dev/null @@ -1,351 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import Completion._ -import scala.collection.mutable.ListBuffer -import scala.reflect.internal.util.StringOps.longestCommonPrefix -import scala.tools.nsc.interactive.Global - -// REPL completor - queries supplied interpreter for valid -// completions based on current contents of buffer. -// TODO: change class name to reflect it's not specific to jline (nor does it depend on it) -class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput { - val global: intp.global.type = intp.global - import global._ - import definitions._ - import rootMirror.{ RootClass, getModuleIfDefined } - import intp.{ debugging } - - // verbosity goes up with consecutive tabs - private var verbosity: Int = 0 - def resetVerbosity() = verbosity = 0 - - def getSymbol(name: String, isModule: Boolean) = ( - if (isModule) getModuleIfDefined(name) - else getModuleIfDefined(name) - ) - - trait CompilerCompletion { - def tp: Type - def effectiveTp = tp match { - case MethodType(Nil, resType) => resType - case NullaryMethodType(resType) => resType - case _ => tp - } - - // for some reason any's members don't show up in subclasses, which - // we need so 5.<tab> offers asInstanceOf etc. - private def anyMembers = AnyTpe.nonPrivateMembers - def anyRefMethodsToShow = Set("isInstanceOf", "asInstanceOf", "toString") - - def tos(sym: Symbol): String = sym.decodedName - def memberNamed(s: String) = exitingTyper(effectiveTp member newTermName(s)) - - // XXX we'd like to say "filterNot (_.isDeprecated)" but this causes the - // compiler to crash for reasons not yet known. - def members = exitingTyper((effectiveTp.nonPrivateMembers.toList ++ anyMembers) filter (_.isPublic)) - def methods = members.toList filter (_.isMethod) - def packages = members.toList filter (_.hasPackageFlag) - def aliases = members.toList filter (_.isAliasType) - - def memberNames = members map tos - def methodNames = methods map tos - def packageNames = packages map tos - def aliasNames = aliases map tos - } - - object NoTypeCompletion extends TypeMemberCompletion(NoType) { - override def memberNamed(s: String) = NoSymbol - override def members = Nil - override def follow(s: String) = None - override def alternativesFor(id: String) = Nil - } - - object TypeMemberCompletion { - def apply(tp: Type, runtimeType: Type, param: NamedParam): TypeMemberCompletion = { - new TypeMemberCompletion(tp) { - var upgraded = false - lazy val upgrade = { - intp rebind param - intp.reporter.printMessage("\nRebinding stable value %s from %s to %s".format(param.name, tp, param.tpe)) - upgraded = true - new TypeMemberCompletion(runtimeType) - } - override def completions(verbosity: Int) = { - super.completions(verbosity) ++ ( - if (verbosity == 0) Nil - else upgrade.completions(verbosity) - ) - } - override def follow(s: String) = super.follow(s) orElse { - if (upgraded) upgrade.follow(s) - else None - } - override def alternativesFor(id: String) = super.alternativesFor(id) ++ ( - if (upgraded) upgrade.alternativesFor(id) - else Nil - ) distinct - } - } - def apply(tp: Type): TypeMemberCompletion = { - if (tp eq NoType) NoTypeCompletion - else if (tp.typeSymbol.isPackageClass) new PackageCompletion(tp) - else new TypeMemberCompletion(tp) - } - def imported(tp: Type) = new ImportCompletion(tp) - } - - class TypeMemberCompletion(val tp: Type) extends CompletionAware - with CompilerCompletion { - def excludeEndsWith: List[String] = Nil - def excludeStartsWith: List[String] = List("<") // <byname>, <repeated>, etc. - def excludeNames: List[String] = (anyref.methodNames filterNot anyRefMethodsToShow) :+ "_root_" - - def methodSignatureString(sym: Symbol) = { - IMain stripString exitingTyper(new MethodSymbolOutput(sym).methodString()) - } - - def exclude(name: String): Boolean = ( - (name contains "$") || - (excludeNames contains name) || - (excludeEndsWith exists (name endsWith _)) || - (excludeStartsWith exists (name startsWith _)) - ) - def filtered(xs: List[String]) = xs filterNot exclude distinct - - def completions(verbosity: Int) = - debugging(tp + " completions ==> ")(filtered(memberNames)) - - override def follow(s: String): Option[CompletionAware] = - debugging(tp + " -> '" + s + "' ==> ")(Some(TypeMemberCompletion(memberNamed(s).tpe)) filterNot (_ eq NoTypeCompletion)) - - override def alternativesFor(id: String): List[String] = - debugging(id + " alternatives ==> ") { - val alts = members filter (x => x.isMethod && tos(x) == id) map methodSignatureString - - if (alts.nonEmpty) "" :: alts else Nil - } - - override def toString = "%s (%d members)".format(tp, members.size) - } - - class PackageCompletion(tp: Type) extends TypeMemberCompletion(tp) { - override def excludeNames = anyref.methodNames - } - - class LiteralCompletion(lit: Literal) extends TypeMemberCompletion(lit.value.tpe) { - override def completions(verbosity: Int) = verbosity match { - case 0 => filtered(memberNames) - case _ => memberNames - } - } - - class ImportCompletion(tp: Type) extends TypeMemberCompletion(tp) { - override def completions(verbosity: Int) = verbosity match { - case 0 => filtered(members filterNot (_.isSetter) map tos) - case _ => super.completions(verbosity) - } - } - - // not for completion but for excluding - object anyref extends TypeMemberCompletion(AnyRefTpe) { } - - // the unqualified vals/defs/etc visible in the repl - object ids extends CompletionAware { - override def completions(verbosity: Int) = intp.unqualifiedIds ++ List("classOf") //, "_root_") - // now we use the compiler for everything. - override def follow(id: String): Option[CompletionAware] = { - if (!completions(0).contains(id)) - return None - - val tpe = intp typeOfExpression id - if (tpe == NoType) - return None - - def default = Some(TypeMemberCompletion(tpe)) - - // only rebinding vals in power mode for now. - if (!isReplPower) default - else intp runtimeClassAndTypeOfTerm id match { - case Some((clazz, runtimeType)) => - val sym = intp.symbolOfTerm(id) - if (sym.isStable) { - val param = new NamedParam.Untyped(id, intp valueOfTerm id orNull) - Some(TypeMemberCompletion(tpe, runtimeType, param)) - } - else default - case _ => - default - } - } - override def toString = "<repl ids> (%s)".format(completions(0).size) - } - - // user-issued wildcard imports like "import global._" or "import String._" - private def imported = intp.sessionWildcards map TypeMemberCompletion.imported - - // literal Ints, Strings, etc. - object literals extends CompletionAware { - def simpleParse(code: String): Option[Tree] = newUnitParser(code).parseStats().lastOption - def completions(verbosity: Int) = Nil - - override def follow(id: String) = simpleParse(id).flatMap { - case x: Literal => Some(new LiteralCompletion(x)) - case _ => None - } - } - - // top level packages - object rootClass extends TypeMemberCompletion(RootClass.tpe) { - override def completions(verbosity: Int) = super.completions(verbosity) :+ "_root_" - override def follow(id: String) = id match { - case "_root_" => Some(this) - case _ => super.follow(id) - } - } - // members of Predef - object predef extends TypeMemberCompletion(PredefModule.tpe) { - override def excludeEndsWith = super.excludeEndsWith ++ List("Wrapper", "ArrayOps") - override def excludeStartsWith = super.excludeStartsWith ++ List("wrap") - override def excludeNames = anyref.methodNames - - override def exclude(name: String) = super.exclude(name) || ( - (name contains "2") - ) - - override def completions(verbosity: Int) = verbosity match { - case 0 => Nil - case _ => super.completions(verbosity) - } - } - // members of scala.* - object scalalang extends PackageCompletion(ScalaPackage.tpe) { - def arityClasses = List("Product", "Tuple", "Function") - def skipArity(name: String) = arityClasses exists (x => name != x && (name startsWith x)) - override def exclude(name: String) = super.exclude(name) || ( - skipArity(name) - ) - - override def completions(verbosity: Int) = verbosity match { - case 0 => filtered(packageNames ++ aliasNames) - case _ => super.completions(verbosity) - } - } - // members of java.lang.* - object javalang extends PackageCompletion(JavaLangPackage.tpe) { - override lazy val excludeEndsWith = super.excludeEndsWith ++ List("Exception", "Error") - override lazy val excludeStartsWith = super.excludeStartsWith ++ List("CharacterData") - - override def completions(verbosity: Int) = verbosity match { - case 0 => filtered(packageNames) - case _ => super.completions(verbosity) - } - } - - // the list of completion aware objects which should be consulted - // for top level unqualified, it's too noisy to let much in. - lazy val topLevelBase: List[CompletionAware] = List(ids, rootClass, predef, scalalang, javalang, literals) - def topLevel = topLevelBase ++ imported - def topLevelThreshold = 50 - - // the first tier of top level objects (doesn't include file completion) - def topLevelFor(parsed: Parsed): List[String] = { - val buf = new ListBuffer[String] - topLevel foreach { ca => - buf ++= (ca completionsFor parsed) - - if (buf.size > topLevelThreshold) - return buf.toList.sorted - } - buf.toList - } - - // the most recent result - def lastResult = Forwarder(() => ids follow intp.mostRecentVar) - - def lastResultFor(parsed: Parsed) = { - /** The logic is a little tortured right now because normally '.' is - * ignored as a delimiter, but on .<tab> it needs to be propagated. - */ - val xs = lastResult completionsFor parsed - if (parsed.isEmpty) xs map ("." + _) else xs - } - - def completer(): ScalaCompleter = new JLineTabCompletion - - /** This gets a little bit hairy. It's no small feat delegating everything - * and also keeping track of exactly where the cursor is and where it's supposed - * to end up. The alternatives mechanism is a little hacky: if there is an empty - * string in the list of completions, that means we are expanding a unique - * completion, so don't update the "last" buffer because it'll be wrong. - */ - class JLineTabCompletion extends ScalaCompleter { - // For recording the buffer on the last tab hit - private var lastBuf: String = "" - private var lastCursor: Int = -1 - - // Does this represent two consecutive tabs? - def isConsecutiveTabs(buf: String, cursor: Int) = - cursor == lastCursor && buf == lastBuf - - // This is jline's entry point for completion. - override def complete(buf: String, cursor: Int): Candidates = { - verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0 - repldbg(f"%ncomplete($buf, $cursor%d) last = ($lastBuf, $lastCursor%d), verbosity: $verbosity") - // we don't try lower priority completions unless higher ones return no results. - def tryCompletion(p: Parsed, completionFunction: Parsed => List[String]): Option[Candidates] = { - val winners = completionFunction(p) - if (winners.isEmpty) - return None - val newCursor = - if (winners contains "") p.cursor - else { - val advance = longestCommonPrefix(winners) - lastCursor = p.position + advance.length - lastBuf = (buf take p.position) + advance - repldbg(s"tryCompletion($p, _) lastBuf = $lastBuf, lastCursor = $lastCursor, p.position = ${p.position}") - p.position - } - - Some(Candidates(newCursor, winners)) - } - - def mkDotted = Parsed.dotted(buf, cursor) withVerbosity verbosity - - // a single dot is special cased to completion on the previous result - def lastResultCompletion = - if (!looksLikeInvocation(buf)) None - else tryCompletion(Parsed.dotted(buf drop 1, cursor), lastResultFor) - - def tryAll = ( - lastResultCompletion - orElse tryCompletion(mkDotted, topLevelFor) - getOrElse Candidates(cursor, Nil) - ) - - /** - * This is the kickoff point for all manner of theoretically - * possible compiler unhappiness. The fault may be here or - * elsewhere, but we don't want to crash the repl regardless. - * The compiler makes it impossible to avoid catching Throwable - * with its unfortunate tendency to throw java.lang.Errors and - * AssertionErrors as the hats drop. We take two swings at it - * because there are some spots which like to throw an assertion - * once, then work after that. Yeah, what can I say. - */ - try tryAll - catch { case ex: Throwable => - repldbg("Error: complete(%s, %s) provoked".format(buf, cursor) + ex) - Candidates(cursor, - if (isReplDebug) List("<error:" + ex + ">") - else Nil - ) - } - } - } -} diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala index 9ad9479d05..034437fe5c 100644 --- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala @@ -2,131 +2,76 @@ * Copyright 2005-2013 LAMP/EPFL * @author Paul Phillips */ - package scala package tools.nsc package interpreter -import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } -import scala.tools.asm.Opcodes -import scala.tools.nsc.util.ScalaClassLoader +import scala.language.reflectiveCalls + +import java.lang.{ Iterable => JIterable } +import scala.reflect.internal.util.ScalaClassLoader import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, PrintWriter, StringWriter, Writer } import java.util.{ Locale } import java.util.concurrent.ConcurrentLinkedQueue -import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, +import javax.tools.{ Diagnostic, DiagnosticListener, ForwardingJavaFileManager, JavaFileManager, JavaFileObject, SimpleJavaFileObject, StandardLocation } -import scala.reflect.io.{ AbstractFile, Directory, File, Path } +import scala.reflect.io.File import scala.io.Source import scala.util.{ Try, Success, Failure } import scala.util.Properties.{ lineSeparator => EOL } -import scala.util.matching.Regex import scala.collection.JavaConverters._ import scala.collection.generic.Clearable import java.net.URL -import scala.language.reflectiveCalls -import PartialFunction.{ cond => when } -import Javap._ +import Javap.{ JpResult, JpError, Showable, helper, toolArgs, DefaultOptions } -/** Javap command implementation. Supports platform tool for Java 6 or 7+. - * Adds a few options for REPL world, to show bodies of `App` classes and closures. +/** Javap command implementation. */ class JavapClass( val loader: ScalaClassLoader, val printWriter: PrintWriter, - intp: Option[IMain] = None + intp: IMain ) extends Javap { - import JavapTool.ToolArgs import JavapClass._ lazy val tool = JavapTool() - /** Run the tool. Option args start with "-", except that "-" itself - * denotes the last REPL result. - * The default options are "-protected -verbose". - * Byte data for filename args is retrieved with findBytes. - * @return results for invoking JpResult.show() - */ def apply(args: Seq[String]): List[JpResult] = { - val (options, classes) = args partition (s => (s startsWith "-") && s.length > 1) - val (flags, upgraded) = upgrade(options) - import flags.{ app, fun, help, raw } - - val targets = if (fun && !help) FunFinder(loader, intp).funs(classes) else classes + val (options0, targets) = args partition (s => (s startsWith "-") && s.length > 1) + val (options, filter) = { + val (opts, flag) = toolArgs(options0) + (if (opts.isEmpty) DefaultOptions else opts, flag) + } - if (help || classes.isEmpty) - List(JpResult(JavapTool.helper(printWriter))) - else if (targets.isEmpty) - List(JpResult("No closures found.")) + if ((options contains "-help") || targets.isEmpty) + List(JpResult(helper(printWriter))) else - tool(raw, upgraded)(targets map (targeted(_, app))) // JavapTool.apply + tool(options, filter)(targets map targeted) } - /** Cull our tool options. */ - private def upgrade(options: Seq[String]): (ToolArgs, Seq[String]) = - ToolArgs fromArgs options match { - case (t, s) if s.nonEmpty => (t, s) - case (t, s) => (t, JavapTool.DefaultOptions) - } - /** Associate the requested path with a possibly failed or empty array of bytes. */ - private def targeted(path: String, app: Boolean): (String, Try[Array[Byte]]) = - bytesFor(path, app) match { + private def targeted(path: String): (String, Try[Array[Byte]]) = + bytesFor(path) match { case Success((target, bytes)) => (target, Try(bytes)) case f: Failure[_] => (path, Failure(f.exception)) } - /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). + /** Find bytes. Handle "-", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). * @return the path to use for filtering, and the byte array */ - private def bytesFor(path: String, app: Boolean) = Try { - def last = intp.get.mostRecentVar // fail if no intp + private def bytesFor(path: String) = Try { val req = path match { - case "-" => last + case "-" => intp.mostRecentVar case HashSplit(prefix, _) if prefix != null => prefix case HashSplit(_, member) if member != null => member case s => s } - val targetedBytes = if (app) findAppBody(req) else (path, findBytes(req)) - targetedBytes match { + (path, findBytes(req)) match { case (_, bytes) if bytes.isEmpty => throw new FileNotFoundException(s"Could not find class bytes for '$path'") case ok => ok } } - private def findAppBody(path: String): (String, Array[Byte]) = { - // is this new style delayedEndpoint? then find it. - // the name test is naive. could add $mangled path. - // assumes only the first match is of interest (because only one endpoint is generated). - def findNewStyle(bytes: Array[Byte]) = { - import scala.tools.asm.ClassReader - //foo/Bar.delayedEndpoint$foo$Bar$1 - val endpoint = "delayedEndpoint".r.unanchored - def isEndPoint(s: String) = (s contains '$') && when(s) { case endpoint() => true } - new ClassReader(bytes) withMethods { methods => - methods collectFirst { case m if isEndPoint(m.name) => m.name } - } - } - // try new style, and add foo#delayedEndpoint$bar$1 to filter on the endpoint - def asNewStyle(bytes: Array[Byte]) = Some(bytes) filter (_.nonEmpty) flatMap { bs => - findNewStyle(bs) map (n => (s"$path#$n", bs)) - } - // use old style, and add foo# to filter on apply method - def asOldStyle = { - def asAppBody(s: String) = { - val (cls, fix) = s.splitSuffix - s"${cls}$$delayedInit$$body${fix}" - } - val oldStyle = asAppBody(path) - val oldBytes = findBytes(oldStyle) - if (oldBytes.nonEmpty) (s"$oldStyle#", oldBytes) - else (path, oldBytes) - } - - val pathBytes = findBytes(path) - asNewStyle(pathBytes) getOrElse asOldStyle - } - def findBytes(path: String): Array[Byte] = tryFile(path) getOrElse tryClass(path) /** Assume the string is a path and try to find the classfile it represents. @@ -151,7 +96,7 @@ class JavapClass( if (0 until s.length - 1 contains i) { val name = s substring (0, i) val sufx = s substring i - val tran = intp flatMap (_ translatePath name) + val tran = intp translatePath name def loadableOrNone(strip: Boolean) = { def suffix(strip: Boolean)(x: String) = (if (strip && (x endsWith "$")) x.init else x) + sufx @@ -169,13 +114,13 @@ class JavapClass( // if repl, translate the name to something replish // (for translate, would be nicer to get the sym and ask .isClass, // instead of translatePath and then asking did I get a class back) - val q = if (intp.isEmpty) p else ( + val q = ( // only simple names get the scope treatment Some(p) filter (_ contains '.') // take path as a Name in scope - orElse (intp flatMap (_ translatePath p) filter loadable) + orElse (intp translatePath p filter loadable) // take path as a Name in scope and find its enclosing class - orElse (intp flatMap (_ translateEnclosingClass p) filter loadable) + orElse (intp translateEnclosingClass p filter loadable) // take path as a synthetic derived from some Name in scope orElse desynthesize(p) // just try it plain @@ -184,16 +129,10 @@ class JavapClass( load(q) } - /** Base class for javap tool adapters for java 6 and 7. */ - abstract class JavapTool { + class JavapTool { type ByteAry = Array[Byte] type Input = Tuple2[String, Try[ByteAry]] - /** Run the tool. */ - def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] - - // Since the tool is loaded by reflection, check for catastrophic failure. - protected def failed: Boolean implicit protected class Failer[A](a: =>A) { def orFailed[B >: A](b: =>B) = if (failed) b else a } @@ -209,8 +148,7 @@ class JavapClass( } def filterLines(target: String, text: String): String = { - // take Foo# as Foo#apply for purposes of filtering. Useful for -fun Foo#; - // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? + // take Foo# as Foo#apply for purposes of filtering. val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } var filtering = false // true if in region matching filter // turn filtering on/off given the pattern of interest @@ -253,62 +191,6 @@ class JavapClass( sw.toString } - /** Create a Showable with output massage. - * @param raw show ugly repl names - * @param target attempt to filter output to show region of interest - * @param preamble other messages to output - */ - def showWithPreamble(raw: Boolean, target: String, preamble: String = ""): Showable = - new Showable { - private def writeLines() = filterLines(target, preamble + written) - val output = writeLines() - - // ReplStrippingWriter clips and scrubs on write(String) - // circumvent it by write(mw, 0, mw.length) or wrap it in withoutUnwrapping - def show() = - if (raw && intp.isDefined) intp.get withoutUnwrapping { printWriter.write(output, 0, output.length) } - else intp.get withoutTruncating(printWriter write output) - } - } - - class JavapTool6 extends JavapTool { - import JavapTool._ - val EnvClass = loader.tryToInitializeClass[FakeEnvironment](Env).orNull - val PrinterClass = loader.tryToInitializeClass[FakePrinter](Printer).orNull - override protected def failed = (EnvClass eq null) || (PrinterClass eq null) - - val PrinterCtr = PrinterClass.getConstructor(classOf[InputStream], classOf[PrintWriter], EnvClass) orFailed null - val printWrapper = new PrintWriter(writer) - def newPrinter(in: InputStream, env: FakeEnvironment): FakePrinter = - PrinterCtr.newInstance(in, printWrapper, env) orFailed null - def showable(raw: Boolean, target: String, fp: FakePrinter): Showable = { - fp.asInstanceOf[{ def print(): Unit }].print() // run tool and flush to buffer - printWrapper.flush() // just in case - showWithPreamble(raw, target) - } - - lazy val parser = new JpOptions - def newEnv(opts: Seq[String]): FakeEnvironment = { - def result = { - val env: FakeEnvironment = EnvClass.newInstance() - parser(opts) foreach { case (name, value) => - val field = EnvClass getDeclaredField name - field setAccessible true - field.set(env, value.asInstanceOf[AnyRef]) - } - env - } - result orFailed null - } - - override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = - (inputs map { - case (klass, Success(ba)) => JpResult(showable(raw, klass, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) - case (_, Failure(e)) => JpResult(e.toString) - }).toList orFailed List(noToolError) - } - - class JavapTool7 extends JavapTool { import JavapTool._ type Task = { def call(): Boolean // true = ok @@ -319,8 +201,9 @@ class JavapClass( //object TaskResult extends Enumeration { // val Ok, Error, CmdErr, SysErr, Abnormal = Value //} - val TaskClass = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull - override protected def failed = TaskClass eq null + val TaskClass = loader.tryToInitializeClass[Task](JavapTask).orNull + // Since the tool is loaded by reflection, check for catastrophic failure. + protected def failed = TaskClass eq null val TaskCtor = TaskClass.getConstructor( classOf[Writer], @@ -331,7 +214,6 @@ class JavapClass( ) orFailed null class JavaReporter extends DiagnosticListener[JavaFileObject] with Clearable { - import scala.collection.mutable.{ ArrayBuffer, SynchronizedBuffer } type D = Diagnostic[_ <: JavaFileObject] val diagnostics = new ConcurrentLinkedQueue[D] override def report(d: Diagnostic[_ <: JavaFileObject]) { @@ -343,12 +225,9 @@ class JavapClass( */ def messages(implicit locale: Locale = null) = diagnostics.asScala.map(_ getMessage locale).toList - // don't filter this message if raw, since the names are likely to differ - private val container = "Binary file .* contains .*".r - def reportable(raw: Boolean): String = { - val m = if (raw) messages else messages filterNot (when(_) { case container() => true }) + def reportable(): String = { clear() - if (m.nonEmpty) m mkString ("", EOL, EOL) else "" + if (messages.nonEmpty) messages mkString ("", EOL, EOL) else "" } } val reporter = new JavaReporter @@ -403,23 +282,33 @@ class JavapClass( } def fileManager(inputs: Seq[Input]) = new JavapFileManager(inputs)() - // show tool messages and tool output, with output massage - def showable(raw: Boolean, target: String): Showable = showWithPreamble(raw, target, reporter.reportable(raw)) + /** Create a Showable to show tool messages and tool output, with output massage. + * @param target attempt to filter output to show region of interest + * @param filter whether to strip REPL names + */ + def showable(target: String, filter: Boolean): Showable = + new Showable { + val output = filterLines(target, s"${reporter.reportable()}${written}") + def show() = + if (filter) intp.withoutTruncating(printWriter.write(output)) + else intp.withoutUnwrapping(printWriter.write(output, 0, output.length)) + } // eventually, use the tool interface def task(options: Seq[String], classes: Seq[String], inputs: Seq[Input]): Task = { //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). //getTask(writer, fileManager, reporter, options.asJava, classes.asJava) - TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, classes.asJava) + val toolopts = options filter (_ != "-filter") + TaskCtor.newInstance(writer, fileManager(inputs), reporter, toolopts.asJava, classes.asJava) .orFailed (throw new IllegalStateException) } // a result per input - private def applyOne(raw: Boolean, options: Seq[String], klass: String, inputs: Seq[Input]): Try[JpResult] = + private def applyOne(options: Seq[String], filter: Boolean, klass: String, inputs: Seq[Input]): Try[JpResult] = Try { task(options, Seq(klass), inputs).call() } map { - case true => JpResult(showable(raw, klass)) - case _ => JpResult(reporter.reportable(raw)) + case true => JpResult(showable(klass, filter)) + case _ => JpResult(reporter.reportable()) } recoverWith { case e: java.lang.reflect.InvocationTargetException => e.getCause match { case t: IllegalArgumentException => Success(JpResult(t.getMessage)) // bad option @@ -428,150 +317,35 @@ class JavapClass( } lastly { reporter.clear() } - override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { - case (klass, Success(_)) => applyOne(raw, options, klass, inputs).get + /** Run the tool. */ + def apply(options: Seq[String], filter: Boolean)(inputs: Seq[Input]): List[JpResult] = (inputs map { + case (klass, Success(_)) => applyOne(options, filter, klass, inputs).get case (_, Failure(e)) => JpResult(e.toString) }).toList orFailed List(noToolError) } object JavapTool { // >= 1.7 - val Tool = "com.sun.tools.javap.JavapTask" - - // < 1.7 - val Env = "sun.tools.javap.JavapEnvironment" - val Printer = "sun.tools.javap.JavapPrinter" - // "documentation" - type FakeEnvironment = AnyRef - type FakePrinter = AnyRef - - // support JavapEnvironment - class JpOptions { - private object Access { - final val PRIVATE = 0 - final val PROTECTED = 1 - final val PACKAGE = 2 - final val PUBLIC = 3 - } - private val envActionMap: Map[String, (String, Any)] = { - val map = Map( - "-l" -> (("showLineAndLocal", true)), - "-c" -> (("showDisassembled", true)), - "-s" -> (("showInternalSigs", true)), - "-verbose" -> (("showVerbose", true)), - "-private" -> (("showAccess", Access.PRIVATE)), - "-package" -> (("showAccess", Access.PACKAGE)), - "-protected" -> (("showAccess", Access.PROTECTED)), - "-public" -> (("showAccess", Access.PUBLIC)), - "-all" -> (("showallAttr", true)) - ) - map ++ List( - "-v" -> map("-verbose"), - "-p" -> map("-private") - ) - } - def apply(opts: Seq[String]): Seq[(String, Any)] = { - opts flatMap { opt => - envActionMap get opt match { - case Some(pair) => List(pair) - case _ => - val charOpts = opt.tail.toSeq map ("-" + _) - if (charOpts forall (envActionMap contains _)) - charOpts map envActionMap - else Nil - } - } - } - } - - case class ToolArgs(raw: Boolean = false, help: Boolean = false, app: Boolean = false, fun: Boolean = false) - - object ToolArgs { - def fromArgs(args: Seq[String]): (ToolArgs, Seq[String]) = ((ToolArgs(), Seq[String]()) /: (args flatMap massage)) { - case ((t,others), s) => s match { - case "-fun" => (t copy (fun=true), others :+ "-private") - case "-app" => (t copy (app=true), others) - case "-help" => (t copy (help=true), others) - case "-raw" => (t copy (raw=true), others) - case _ => (t, others :+ s) - } - } - } - - val helps = List( - "usage" -> ":javap [opts] [path or class or -]...", - "-help" -> "Prints this help message", - "-raw" -> "Don't unmangle REPL names", - "-app" -> "Show the DelayedInit body of Apps", - "-fun" -> "Show anonfuns for class or Class#method", - "-verbose/-v" -> "Stack size, number of locals, method args", - "-private/-p" -> "Private classes and members", - "-package" -> "Package-private classes and members", - "-protected" -> "Protected classes and members", - "-public" -> "Public classes and members", - "-l" -> "Line and local variable tables", - "-c" -> "Disassembled code", - "-s" -> "Internal type signatures", - "-sysinfo" -> "System info of class", - "-constants" -> "Static final constants" - ) - - // match prefixes and unpack opts, or -help on failure - def massage(arg: String): Seq[String] = { - require(arg startsWith "-") - // arg matches opt "-foo/-f" if prefix of -foo or exactly -f - val r = """(-[^/]*)(/(-.))?""".r - def maybe(opt: String, s: String): Option[String] = opt match { - // disambiguate by preferring short form - case r(lf,_,sf) if s == sf => Some(sf) - case r(lf,_,sf) if lf startsWith s => Some(lf) - case _ => None - } - def candidates(s: String) = (helps map (h => maybe(h._1, s))).flatten - // one candidate or one single-char candidate - def uniqueOf(maybes: Seq[String]) = { - def single(s: String) = s.length == 2 - if (maybes.length == 1) maybes - else if ((maybes count single) == 1) maybes filter single - else Nil - } - // each optchar must decode to exactly one option - def unpacked(s: String): Try[Seq[String]] = { - val ones = (s drop 1) map { c => - val maybes = uniqueOf(candidates(s"-$c")) - if (maybes.length == 1) Some(maybes.head) else None - } - Try(ones) filter (_ forall (_.isDefined)) map (_.flatten) - } - val res = uniqueOf(candidates(arg)) - if (res.nonEmpty) res - else (unpacked(arg) - getOrElse (Seq("-help"))) // or else someone needs help - } - - def helper(pw: PrintWriter) = new Showable { - def show() = helps foreach (p => pw write "%-12.12s%s%n".format(p._1,p._2)) - } - - val DefaultOptions = List("-protected", "-verbose") + val JavapTask = "com.sun.tools.javap.JavapTask" private def hasClass(cl: ScalaClassLoader, cn: String) = cl.tryToInitializeClass[AnyRef](cn).isDefined - def isAvailable = Seq(Env, Tool) exists (hasClass(loader, _)) + def isAvailable = hasClass(loader, JavapTask) /** Select the tool implementation for this platform. */ - def apply() = if (hasClass(loader, Tool)) new JavapTool7 else new JavapTool6 + def apply() = { + require(isAvailable) + new JavapTool + } } } object JavapClass { - import scala.tools.asm.ClassReader - import scala.tools.asm.tree.{ ClassNode, MethodNode } def apply( loader: ScalaClassLoader = ScalaClassLoader.appLoader, printWriter: PrintWriter = new PrintWriter(System.out, true), - intp: Option[IMain] = None + intp: IMain ) = new JavapClass(loader, printWriter, intp) /** Match foo#bar, both groups are optional (may be null). */ @@ -596,209 +370,29 @@ object JavapClass { } } implicit class ClassLoaderOps(val loader: ScalaClassLoader) extends AnyVal { - private def parentsOf(x: ClassLoader): List[ClassLoader] = if (x == null) Nil else x :: parentsOf(x.getParent) - def parents: List[ClassLoader] = parentsOf(loader) - /* all file locations */ - def locations = { - def alldirs = parents flatMap { - case ucl: ScalaClassLoader.URLClassLoader => ucl.classPathURLs - case jcl: java.net.URLClassLoader => jcl.getURLs - case _ => Nil - } - val dirs = for (d <- alldirs; if d.getProtocol == "file") yield Path(new JFile(d.toURI)) - dirs - } - /* only the file location from which the given class is loaded */ - def locate(k: String): Option[Path] = { - Try { - val klass = try loader loadClass k catch { - case _: NoClassDefFoundError => null // let it snow - } - // cf ScalaClassLoader.originOfClass - klass.getProtectionDomain.getCodeSource.getLocation - } match { - case Success(null) => None - case Success(loc) if loc.isFile => Some(Path(new JFile(loc.toURI))) - case _ => None - } - } /* would classBytes succeed with a nonempty array */ def resourceable(className: String): Boolean = loader.getResource(className.asClassResource) != null - - /* class reader of class bytes */ - def classReader(resource: String): ClassReader = new ClassReader(loader classBytes resource) - } - implicit class `class reader convenience`(val reader: ClassReader) extends AnyVal { - def withMethods[A](f: Seq[MethodNode] => A): A = { - val cls = new ClassNode - reader.accept(cls, 0) - f(cls.methods.asScala) - } - } - implicit class PathOps(val p: Path) extends AnyVal { - import scala.tools.nsc.io.Jar - def isJar = Jar isJarOrZip p - } - implicit class `fun with files`(val f: AbstractFile) extends AnyVal { - def descend(path: Seq[String]): Option[AbstractFile] = { - def lookup(f: AbstractFile, path: Seq[String]): Option[AbstractFile] = path match { - case p if p.isEmpty => Option(f) - case p => Option(f.lookupName(p.head, directory = true)) flatMap (lookup(_, p.tail)) - } - lookup(f, path) - } } implicit class URLOps(val url: URL) extends AnyVal { def isFile: Boolean = url.getProtocol == "file" } - object FunFinder { - def apply(loader: ScalaClassLoader, intp: Option[IMain]) = new FunFinder(loader, intp) - } - // FunFinder.funs(ks) finds anonfuns - class FunFinder(loader: ScalaClassLoader, intp: Option[IMain]) { - - // manglese for closure: typename, $anonfun or lambda, opt method, digits - val closure = """(.*)\$(\$anonfun|lambda)(?:\$+([^$]+))?\$(\d+)""".r - - // manglese for closure - val cleese = "(?:anonfun|lambda)" - - // class k, candidate f without prefix - def isFunOfClass(k: String, f: String) = (s"${Regex quote k}\\$$+$cleese".r findPrefixOf f).nonEmpty - - // class k, candidate f without prefix, method m - def isFunOfMethod(k: String, m: String, f: String) = - (s"${Regex quote k}\\$$+$cleese\\$$+${Regex quote m}\\$$".r findPrefixOf f).nonEmpty - - def isFunOfTarget(target: Target, f: String) = - target.member map (isFunOfMethod(target.name, _, f)) getOrElse isFunOfClass(target.name, f) - - def listFunsInAbsFile(target: Target)(d: AbstractFile) = - for (f <- d; if !f.isDirectory && isFunOfTarget(target, f.name)) yield f.name - - def listFunsInDir(target: Target)(d: Directory) = { - val subdir = Path(target.prefix) - for (f <- (d / subdir).toDirectory.list; if f.isFile && isFunOfTarget(target, f.name)) - yield f.name - } - - def listFunsInJar(target: Target)(f: File) = { - import java.util.jar.JarEntry - import scala.tools.nsc.io.Jar - def maybe(e: JarEntry) = { - val (path, name) = { - val parts = e.getName split "/" - if (parts.length < 2) ("", e.getName) - else (parts.init mkString "/", parts.last) - } - if (path == target.prefix && isFunOfTarget(target, name)) Some(name) else None - } - (new Jar(f) map maybe).flatten - } - def loadable(name: String) = loader resourceable name - case class Target(path: String, member: Option[String], filter: Option[String], isRepl: Boolean, isModule: Boolean) { - val splat = path split "\\." - val name = splat.last - val prefix = if (splat.length > 1) splat.init mkString "/" else "" - val pkg = if (splat.length > 1) splat.init mkString "." else "" - val targetName = s"$name${ if (isModule) "$" else "" }" - } - // translated class, optional member, opt member to filter on, whether it is repl output and a module - def translate(s: String): Target = { - val (k0, m0) = s.splitHashMember - val isModule = k0 endsWith "$" - val k = (k0 stripSuffix "$").asClassName - val member = m0 filter (_.nonEmpty) // take Foo# as no member, not "" - val filter = m0 flatMap { case "" => Some("apply") case _ => None } // take Foo# as filter on apply - // class is either something replish or available to loader - // $line.$read$$etc$Foo#member - ((intp flatMap (_ translatePath k) filter (loadable) map (x => Target(x stripSuffix "$", member, filter, true, isModule))) - // s = "f" and $line.$read$$etc$#f is what we're after, - // ignoring any #member (except take # as filter on #apply) - orElse (intp flatMap (_ translateEnclosingClass k) map (x => Target(x stripSuffix "$", Some(k), filter, true, isModule))) - getOrElse (Target(k, member, filter, false, isModule))) - } - /** Find the classnames of anonfuns associated with k, - * where k may be an available class or a symbol in scope. - */ - def funsOf(selection: String): Seq[String] = { - // class is either something replish or available to loader - val target = translate(selection) - - // reconstitute an anonfun with a package - // if filtered, add the hash back, e.g. pkg.Foo#bar, pkg.Foo$anon$1#apply - def packaged(s: String) = { - val p = if (target.pkg.isEmpty) s else s"${target.pkg}.$s" - target.filter map (p + "#" + _) getOrElse p - } - // find closure classes in repl outdir or try asking the classloader where to look - val fs = - if (target.isRepl) - (intp.get.replOutput.dir descend target.splat.init) map { d => - listFunsInAbsFile(target)(d) map (_.asClassName) map packaged - } - else - loader locate target.path map { - case d if d.isDirectory => listFunsInDir(target)(d.toDirectory) map packaged - case j if j.isJar => listFunsInJar(target)(j.toFile) map packaged - case _ => Nil - } - val res = fs map (_.to[Seq]) getOrElse Seq() - // on second thought, we don't care about lambda method classes, just the impl methods - val rev = - res flatMap { - case x @ closure(_, "lambda", _, _) => lambdaMethod(x, target) - //target.member flatMap (_ => lambdaMethod(x, target)) getOrElse s"${target.name}#$$anonfun" - case x => Some(x) - } - rev - } - // given C$lambda$$g$n for member g and n in 1..N, find the C.accessor$x - // and the C.$anonfun$x it forwards to. - def lambdaMethod(lambda: String, target: Target): Option[String] = { - import scala.tools.asm.ClassReader - import scala.tools.asm.Opcodes.INVOKESTATIC - import scala.tools.asm.tree.{ ClassNode, MethodInsnNode } - def callees(s: String): List[(String, String)] = { - loader classReader s withMethods { ms => - val nonBridgeApplyMethods = ms filter (_.name == "apply") filter (n => (n.access & Opcodes.ACC_BRIDGE) == 0) - val instructions = nonBridgeApplyMethods flatMap (_.instructions.toArray) - instructions.collect { - case i: MethodInsnNode => (i.owner, i.name) - }.toList - } - } - callees(lambda) match { - case (k, _) :: Nil if target.isModule && !(k endsWith "$") => None - case (k, m) :: _ => Some(s"${k}#${m}") - case _ => None - } - } - /** Translate the supplied targets to patterns for anonfuns. - * Pattern is typename $ label [[$]$func] $n where label is $anonfun or lambda, - * and lambda includes the extra dollar, func is a method name, and n is an int. - * The typename for a nested class is dollar notation, Betty$Bippy. - * - * If C has anonfun closure classes, then use C$$anonfun$f$1 (various names, C# filters on apply). - * If C has lambda closure classes, then use C#$anonfun (special-cased by output filter). - */ - def funs(ks: Seq[String]): Seq[String] = ks flatMap funsOf - } } -trait Javap { - def loader: ScalaClassLoader - def printWriter: PrintWriter +abstract class Javap { + /** Run the tool. Option args start with "-", except that "-" itself + * denotes the last REPL result. + * The default options are "-protected -verbose". + * Byte data for filename args is retrieved with findBytes. + * @return results for invoking JpResult.show() + */ def apply(args: Seq[String]): List[Javap.JpResult] - def tryFile(path: String): Option[Array[Byte]] - def tryClass(path: String): Array[Byte] } object Javap { - def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl).JavapTool.isAvailable + def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl, intp = null).JavapTool.isAvailable def apply(path: String): Unit = apply(Seq(path)) - def apply(args: Seq[String]): Unit = JavapClass() apply args foreach (_.show()) + def apply(args: Seq[String]): Unit = JavapClass(intp=null) apply args foreach (_.show()) private[interpreter] trait Showable { def show(): Unit @@ -830,13 +424,70 @@ object Javap { def isError = false def show() = value.show() // output to tool's PrintWriter } + + def toolArgs(args: Seq[String]): (Seq[String], Boolean) = { + val (opts, rest) = args flatMap massage partition (_ != "-filter") + (opts, rest.nonEmpty) + } + + val helps = List( + "usage" -> ":javap [opts] [path or class or -]...", + "-help" -> "Prints this help message", + "-verbose/-v" -> "Stack size, number of locals, method args", + "-private/-p" -> "Private classes and members", + "-package" -> "Package-private classes and members", + "-protected" -> "Protected classes and members", + "-public" -> "Public classes and members", + "-l" -> "Line and local variable tables", + "-c" -> "Disassembled code", + "-s" -> "Internal type signatures", + "-sysinfo" -> "System info of class", + "-constants" -> "Static final constants", + "-filter" -> "Filter REPL machinery from output" + ) + + // match prefixes and unpack opts, or -help on failure + private def massage(arg: String): Seq[String] = { + require(arg startsWith "-") + // arg matches opt "-foo/-f" if prefix of -foo or exactly -f + val r = """(-[^/]*)(?:/(-.))?""".r + def maybe(opt: String, s: String): Option[String] = opt match { + // disambiguate by preferring short form + case r(lf, sf) if s == sf => Some(sf) + case r(lf, sf) if lf startsWith s => Some(lf) + case _ => None + } + def candidates(s: String) = (helps map (h => maybe(h._1, s))).flatten + // one candidate or one single-char candidate + def uniqueOf(maybes: Seq[String]) = { + def single(s: String) = s.length == 2 + if (maybes.length == 1) maybes + else if ((maybes count single) == 1) maybes filter single + else Nil + } + // each optchar must decode to exactly one option + def unpacked(s: String): Try[Seq[String]] = { + val ones = (s drop 1) map { c => + val maybes = uniqueOf(candidates(s"-$c")) + if (maybes.length == 1) Some(maybes.head) else None + } + Try(ones) filter (_ forall (_.isDefined)) map (_.flatten) + } + val res = uniqueOf(candidates(arg)) + if (res.nonEmpty) res + else (unpacked(arg) + getOrElse (Seq("-help"))) // or else someone needs help + } + + def helpText: String = (helps map { case (name, help) => f"$name%-12.12s$help%n" }).mkString + + def helper(pw: PrintWriter) = new Showable { + def show() = pw print helpText + } + + val DefaultOptions = List("-protected", "-verbose") } object NoJavap extends Javap { - import Javap._ - def loader: ScalaClassLoader = getClass.getClassLoader - def printWriter: PrintWriter = new PrintWriter(System.err, true) - def apply(args: Seq[String]): List[JpResult] = Nil - def tryFile(path: String): Option[Array[Byte]] = None - def tryClass(path: String): Array[Byte] = Array() + def apply(args: Seq[String]): List[Javap.JpResult] = Nil } diff --git a/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala b/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala index 9f555aee14..8c3b174513 100644 --- a/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala +++ b/src/repl/scala/tools/nsc/interpreter/LoopCommands.scala @@ -8,10 +8,10 @@ package tools package nsc package interpreter -import scala.collection.{ mutable, immutable } -import mutable.ListBuffer import scala.language.implicitConversions +import scala.collection.mutable.ListBuffer + class ProcessResult(val line: String) { import scala.sys.process._ private val buffer = new ListBuffer[String] diff --git a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala index 4e45f6d615..d6c0dafaf2 100644 --- a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -6,9 +6,10 @@ package scala.tools.nsc package interpreter -import scala.collection.{ mutable, immutable } import scala.language.implicitConversions +import scala.collection.mutable + trait MemberHandlers { val intp: IMain diff --git a/src/repl/scala/tools/nsc/interpreter/NamedParam.scala b/src/repl/scala/tools/nsc/interpreter/NamedParam.scala index a0af72940a..d59b07a452 100644 --- a/src/repl/scala/tools/nsc/interpreter/NamedParam.scala +++ b/src/repl/scala/tools/nsc/interpreter/NamedParam.scala @@ -6,10 +6,11 @@ package scala.tools.nsc package interpreter -import NamedParam._ import scala.language.implicitConversions + +import NamedParam._ import scala.reflect.runtime.{universe => ru} -import scala.reflect.{ClassTag, classTag} +import scala.reflect.{ClassTag} import scala.tools.nsc.typechecker.{ TypeStrings } trait NamedParamCreator { diff --git a/src/repl/scala/tools/nsc/interpreter/Phased.scala b/src/repl/scala/tools/nsc/interpreter/Phased.scala index 1cdbd65949..d1d422ce3e 100644 --- a/src/repl/scala/tools/nsc/interpreter/Phased.scala +++ b/src/repl/scala/tools/nsc/interpreter/Phased.scala @@ -6,7 +6,6 @@ package scala.tools.nsc package interpreter -import scala.collection.immutable import scala.language.implicitConversions /** Mix this into an object and use it as a phasing @@ -89,9 +88,8 @@ trait Phased { lazy val all = List( Parser, Namer, Packageobjects, Typer, Superaccessors, Pickler, Refchecks, - Selectiveanf, Liftcode, Selectivecps, Uncurry, Tailcalls, Specialize, - Explicitouter, Erasure, Lazyvals, Lambdalift, Constructors, Flatten, Mixin, - Cleanup, Delambdafy, Icode, Inliner, Closelim, Dce, Jvm, Terminal + Uncurry, Tailcalls, Specialize, Explicitouter, Erasure, Lazyvals, Lambdalift, + Constructors, Flatten, Mixin, Cleanup, Delambdafy, Jvm, Terminal ) lazy val nameMap = all.map(x => x.name -> x).toMap withDefaultValue NoPhaseName multi = all @@ -110,17 +108,17 @@ trait Phased { case object Namer extends PhaseName case object Packageobjects extends PhaseName case object Typer extends PhaseName + case object Patmat extends PhaseName case object Superaccessors extends PhaseName + case object Extmethods extends PhaseName case object Pickler extends PhaseName case object Refchecks extends PhaseName - case object Selectiveanf extends PhaseName - case object Liftcode extends PhaseName - case object Selectivecps extends PhaseName case object Uncurry extends PhaseName case object Tailcalls extends PhaseName case object Specialize extends PhaseName case object Explicitouter extends PhaseName case object Erasure extends PhaseName + case object PostErasure extends PhaseName case object Lazyvals extends PhaseName case object Lambdalift extends PhaseName case object Constructors extends PhaseName @@ -128,10 +126,6 @@ trait Phased { case object Mixin extends PhaseName case object Cleanup extends PhaseName case object Delambdafy extends PhaseName - case object Icode extends PhaseName - case object Inliner extends PhaseName - case object Closelim extends PhaseName - case object Dce extends PhaseName case object Jvm extends PhaseName case object Terminal extends PhaseName case object NoPhaseName extends PhaseName { diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index a14a60d216..7a24405670 100644 --- a/src/repl/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala @@ -6,14 +6,13 @@ package scala.tools.nsc package interpreter -import scala.collection.{ mutable, immutable } -import scala.util.matching.Regex -import scala.io.Codec -import java.net.{ URL, MalformedURLException } -import io.{ Path } import scala.language.implicitConversions + +import scala.collection.mutable +import scala.io.Codec +import java.net.URL import scala.reflect.runtime.{universe => ru} -import scala.reflect.{ClassTag, classTag} +import scala.reflect.ClassTag /** Collecting some power mode examples. @@ -43,7 +42,7 @@ Lost after 18/flatten { /** A class for methods to be injected into the intp in power mode. */ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, replVals: ReplValsImpl) { - import intp.{ beQuietDuring, typeOfExpression, interpret, parse } + import intp.{ beQuietDuring, parse } import intp.global._ import definitions.{ compilerTypeFromTag, compilerSymbolFromTag} @@ -177,12 +176,10 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re */ class InternalInfo[T](value: Option[T] = None)(implicit typeEvidence: ru.TypeTag[T], runtimeClassEvidence: ClassTag[T]) { private def isSpecialized(s: Symbol) = s.name.toString contains "$mc" - private def isImplClass(s: Symbol) = s.name.toString endsWith "$class" /** Standard noise reduction filter. */ def excludeMember(s: Symbol) = ( isSpecialized(s) - || isImplClass(s) || s.isAnonOrRefinementClass || s.isAnonymousFunction ) diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala index 3a2177a4cb..b9a4054ffc 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -5,12 +5,12 @@ package scala.tools.nsc.interpreter import scala.reflect.internal.util.RangePosition +import scala.reflect.io.AbstractFile import scala.tools.nsc.backend.JavaPlatform +import scala.tools.nsc.util.ClassPath import scala.tools.nsc.{interactive, Settings} -import scala.tools.nsc.io._ import scala.tools.nsc.reporters.StoreReporter -import scala.tools.nsc.util.ClassPath.DefaultJavaContext -import scala.tools.nsc.util.{DirectoryClassPath, MergedClassPath} +import scala.tools.nsc.classpath._ trait PresentationCompilation { self: IMain => @@ -55,8 +55,10 @@ trait PresentationCompilation { * You may downcast the `reporter` to `StoreReporter` to access type errors. */ def newPresentationCompiler(): interactive.Global = { - val replOutClasspath: DirectoryClassPath = new DirectoryClassPath(replOutput.dir, DefaultJavaContext) - val mergedClasspath = new MergedClassPath[AbstractFile](replOutClasspath :: global.platform.classPath :: Nil, DefaultJavaContext) + def mergedFlatClasspath = { + val replOutClasspath = ClassPathFactory.newClassPath(replOutput.dir, settings) + AggregateClassPath(replOutClasspath :: global.platform.classPath :: Nil) + } def copySettings: Settings = { val s = new Settings(_ => () /* ignores "bad option -nc" errors, etc */) s.processArguments(global.settings.recreateArgs, processAll = false) @@ -65,10 +67,11 @@ trait PresentationCompilation { } val storeReporter: StoreReporter = new StoreReporter val interactiveGlobal = new interactive.Global(copySettings, storeReporter) { self => - override lazy val platform: ThisPlatform = new JavaPlatform { - val global: self.type = self - - override def classPath: PlatformClassPath = mergedClasspath + override lazy val platform: ThisPlatform = { + new JavaPlatform { + val global: self.type = self + override private[nsc] lazy val classPath: ClassPath = mergedFlatClasspath + } } } new interactiveGlobal.TyperRun() @@ -78,7 +81,7 @@ trait PresentationCompilation { abstract class PresentationCompileResult { val compiler: scala.tools.nsc.interactive.Global def unit: compiler.RichCompilationUnit - /** The length of synthetic code the precedes the user writtn code */ + /** The length of synthetic code the precedes the user written code */ def preambleLength: Int def cleanup(): Unit = { compiler.askShutdown() diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala index 4b0330aaf7..a912ec9749 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala @@ -4,12 +4,11 @@ */ package scala.tools.nsc.interpreter -import scala.reflect.internal.Flags import scala.reflect.internal.util.StringOps -import scala.tools.nsc.interpreter.Completion.{ScalaCompleter, Candidates} +import scala.tools.nsc.interpreter.Completion.Candidates import scala.util.control.NonFatal -class PresentationCompilerCompleter(intp: IMain) extends Completion with ScalaCompleter { +class PresentationCompilerCompleter(intp: IMain) extends Completion { import PresentationCompilerCompleter._ import intp.{PresentationCompileResult => Result} @@ -20,7 +19,6 @@ class PresentationCompilerCompleter(intp: IMain) extends Completion with ScalaCo private var lastCommonPrefixCompletion: Option[String] = None def resetVerbosity(): Unit = { tabCount = 0 ; lastRequest = NoRequest } - def completer(): ScalaCompleter = this // A convenience for testing def complete(before: String, after: String = ""): Candidates = complete(before + after, before.length) diff --git a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala index 07d619bca5..cf055e0758 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala @@ -30,37 +30,5 @@ trait ReplGlobal extends Global { val virtualDirectory = globalSettings.outputDirs.getSingleOutput.get new util.AbstractFileClassLoader(virtualDirectory, loader) {} } - - override def newTyper(context: Context): Typer = new Typer(context) { - override def typed(tree: Tree, mode: Mode, pt: Type): Tree = { - val res = super.typed(tree, mode, pt) - tree match { - case Ident(name) if !tree.symbol.hasPackageFlag && !name.toString.startsWith("$") => - repldbg("typed %s: %s".format(name, res.tpe)) - case _ => - } - res - } - } - } - - object replPhase extends SubComponent { - val global: ReplGlobal.this.type = ReplGlobal.this - val phaseName = "repl" - val runsAfter = List[String]("typer") - val runsRightAfter = None - def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) { - def apply(unit: CompilationUnit) { - repldbg("Running replPhase on " + unit.body) - // newNamer(rootContext(unit)).enterSym(unit.body) - } - } - // add to initial or terminal phase to sanity check Run at construction - override val requires = List("typer") // ensure they didn't -Ystop-after:parser - } - - override protected def computePhaseDescriptors: List[SubComponent] = { - addToPhasesSet(replPhase, "repl") - super.computePhaseDescriptors } } diff --git a/src/repl/scala/tools/nsc/interpreter/RichClass.scala b/src/repl/scala/tools/nsc/interpreter/RichClass.scala index 36cdf65510..df900d1436 100644 --- a/src/repl/scala/tools/nsc/interpreter/RichClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/RichClass.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package interpreter -import scala.reflect.{ ClassTag, classTag } +import scala.reflect.ClassTag class RichClass[T](val clazz: Class[T]) { def toTag: ClassTag[T] = ClassTag[T](clazz) diff --git a/src/repl/scala/tools/nsc/interpreter/package.scala b/src/repl/scala/tools/nsc/interpreter/package.scala index 7934d819b4..97b32bfa86 100644 --- a/src/repl/scala/tools/nsc/interpreter/package.scala +++ b/src/repl/scala/tools/nsc/interpreter/package.scala @@ -88,9 +88,6 @@ package object interpreter extends ReplConfig with ReplStrings { } } - if (filtered.isEmpty) - return "No implicits have been imported other than those in Predef." - filtered foreach { case (source, syms) => p("/* " + syms.size + " implicit members imported from " + source.fullName + " */") @@ -126,7 +123,14 @@ package object interpreter extends ReplConfig with ReplStrings { } p("") } - "" + + if (filtered.nonEmpty) + "" // side-effects above + else if (global.settings.nopredef || global.settings.noimports) + "No implicits have been imported." + else + "No implicits have been imported other than those in Predef." + } def kindCommandInternal(expr: String, verbose: Boolean): Unit = { diff --git a/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala b/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala index 504d0d30ee..92bf9d1df4 100644 --- a/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala @@ -8,7 +8,6 @@ package interpreter package session import scala.collection.mutable.{ Buffer, ListBuffer } -import scala.collection.JavaConverters._ class SimpleHistory extends History { private var _index: Int = 0 |