diff options
Diffstat (limited to 'src/compiler/scala')
56 files changed, 55 insertions, 5930 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index c0f611daa7..7ee3ee551f 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -999,7 +999,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) object typeDeconstruct extends { val global: Global.this.type = Global.this - } with interpreter.StructuredTypeStrings + } with typechecker.StructuredTypeStrings /** There are common error conditions where when the exception hits * here, currentRun.currentUnit is null. This robs us of the knowledge diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala deleted file mode 100644 index 434f19f21b..0000000000 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ /dev/null @@ -1,12 +0,0 @@ -package scala.tools.nsc - -import interpreter._ -import java.io._ - -/** A compatibility stub. - */ -@deprecated("Use a class in the scala.tools.nsc.interpreter package.", "2.9.0") -class Interpreter(settings: Settings, out: PrintWriter) extends IMain(settings, out) { - def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true)) - def this() = this(new Settings()) -}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala deleted file mode 100644 index a0be3f4fdb..0000000000 --- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala +++ /dev/null @@ -1,12 +0,0 @@ -package scala.tools.nsc - -import interpreter._ -import java.io._ - -/** A compatibility stub. - */ -@deprecated("Use a class in the scala.tools.nsc.interpreter package.", "2.9.0") -class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) extends ILoop(in0, out) { - def this(in0: BufferedReader, out: PrintWriter) = this(Some(in0), out) - def this() = this(None, new PrintWriter(scala.Console.out)) -} diff --git a/src/compiler/scala/tools/nsc/MainGenericRunner.scala b/src/compiler/scala/tools/nsc/MainGenericRunner.scala deleted file mode 100644 index adb03ca374..0000000000 --- a/src/compiler/scala/tools/nsc/MainGenericRunner.scala +++ /dev/null @@ -1,106 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2006-2013 LAMP/EPFL - * @author Lex Spoon - */ - -package scala.tools.nsc - -import io.{ File } -import util.{ ClassPath, ScalaClassLoader } -import Properties.{ versionString, copyrightString } -import interpreter.{ ILoop } -import GenericRunnerCommand._ - -object JarRunner extends CommonRunner { - def runJar(settings: GenericRunnerSettings, jarPath: String, arguments: Seq[String]): Either[Throwable, Boolean] = { - val jar = new io.Jar(jarPath) - val mainClass = jar.mainClass getOrElse sys.error("Cannot find main class for jar: " + jarPath) - val jarURLs = ClassPath expandManifestPath jarPath - val urls = if (jarURLs.isEmpty) File(jarPath).toURL +: settings.classpathURLs else jarURLs - - if (settings.Ylogcp.value) { - Console.err.println("Running jar with these URLs as the classpath:") - urls foreach println - } - - runAndCatch(urls, mainClass, arguments) - } -} - -/** An object that runs Scala code. It has three possible - * sources for the code to run: pre-compiled code, a script file, - * or interactive entry. - */ -class MainGenericRunner { - def errorFn(ex: Throwable): Boolean = { - ex.printStackTrace() - false - } - def errorFn(str: String): Boolean = { - Console.err println str - false - } - - def process(args: Array[String]): Boolean = { - val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x)) - import command.{ settings, howToRun, thingToRun } - def sampleCompiler = new Global(settings) // def so its not created unless needed - - if (!command.ok) return errorFn("\n" + command.shortUsageMsg) - else if (settings.version.value) return errorFn("Scala code runner %s -- %s".format(versionString, copyrightString)) - else if (command.shouldStopWithInfo) return errorFn(command getInfoMessage sampleCompiler) - - def isE = !settings.execute.isDefault - def dashe = settings.execute.value - - def isI = !settings.loadfiles.isDefault - def dashi = settings.loadfiles.value - - // Deadlocks on startup under -i unless we disable async. - if (isI) - settings.Yreplsync.value = true - - def combinedCode = { - val files = if (isI) dashi map (file => File(file).slurp()) else Nil - val str = if (isE) List(dashe) else Nil - - files ++ str mkString "\n\n" - } - - def runTarget(): Either[Throwable, Boolean] = howToRun match { - case AsObject => - ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments) - case AsScript => - ScriptRunner.runScriptAndCatch(settings, thingToRun, command.arguments) - case AsJar => - JarRunner.runJar(settings, thingToRun, command.arguments) - case Error => - Right(false) - case _ => - // We start the repl when no arguments are given. - Right(new ILoop process settings) - } - - /** If -e and -i were both given, we want to execute the -e code after the - * -i files have been included, so they are read into strings and prepended to - * the code given in -e. The -i option is documented to only make sense - * interactively so this is a pretty reasonable assumption. - * - * This all needs a rewrite though. - */ - if (isE) { - ScriptRunner.runCommand(settings, combinedCode, thingToRun +: command.arguments) - } - else runTarget() match { - case Left(ex) => errorFn(ex) - case Right(b) => b - } - } -} - -object MainGenericRunner extends MainGenericRunner { - def main(args: Array[String]) { - if (!process(args)) - sys.exit(1) - } -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index 941ccd9a2d..c1cd3204e0 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -9,7 +9,6 @@ package backend.jvm import java.io.{ DataOutputStream, FileOutputStream, OutputStream, File => JFile } import scala.tools.nsc.io._ import scala.tools.nsc.util.ScalaClassLoader -import scala.tools.util.{ Javap, JavapClass } import java.util.jar.Attributes.Name import scala.language.postfixOps @@ -59,35 +58,6 @@ trait BytecodeWriters { override def close() = writer.close() } - /** To be mixed-in with the BytecodeWriter that generates - * the class file to be disassembled. - */ - trait JavapBytecodeWriter extends BytecodeWriter { - val baseDir = Directory(settings.Ygenjavap.value).createDirectory() - val cl = ScalaClassLoader.appLoader - - def emitJavap(classFile: AbstractFile, javapFile: File) { - val pw = javapFile.printWriter() - try { - val javap = new JavapClass(cl, pw) { - override def findBytes(path: String): Array[Byte] = classFile.toByteArray - } - javap(Seq("-verbose", "-protected", classFile.name)) foreach (_.show()) - } finally pw.close() - } - abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { - super.writeClass(label, jclassName, jclassBytes, sym) - - val classFile = getFile(sym, jclassName, ".class") - val segments = jclassName.split("[./]") - val javapFile = segments.foldLeft(baseDir: Path)(_ / _) changeExtension "javap" toFile; - javapFile.parent.createDirectory() - - if (Javap.isAvailable(cl)) emitJavap(classFile, javapFile) - else warning("No javap on classpath, skipping javap output.") - } - } - trait ClassBytecodeWriter extends BytecodeWriter { def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], sym: Symbol) { val outfile = getFile(sym, jclassName, ".class") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 388efb4625..4a3d1805d9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -72,19 +72,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { new DirectToJarfileWriter(f.file) case _ => - import scala.tools.util.Javap - if (settings.Ygenjavap.isDefault) { - if(settings.Ydumpclasses.isDefault) - new ClassBytecodeWriter { } - else - new ClassBytecodeWriter with DumpBytecodeWriter { } - } - else if (Javap.isAvailable()) new ClassBytecodeWriter with JavapBytecodeWriter { } - else { - warning("No javap on classpath, skipping javap output.") + if (settings.Ydumpclasses.isDefault) new ClassBytecodeWriter { } - } - + else + new ClassBytecodeWriter with DumpBytecodeWriter { } // TODO A ScalapBytecodeWriter could take asm.util.Textifier as starting point. // Three areas where javap ouput is less than ideal (e.g. when comparing versions of the same classfile) are: // (a) unreadable pickle; @@ -2519,7 +2510,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { if (nextBlock != whereto) jcode goTo labels(whereto) // SI-6102: Determine whether eliding this JUMP results in an empty range being covered by some EH. - // If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range" + // If so, emit a NOP in place of the elided JUMP, to avoid "java.lang.ClassFormatError: Illegal exception table range" else if (newNormal.isJumpOnly(b) && m.exh.exists(eh => eh.covers(b))) { debugwarn("Had a jump only block that wasn't collapsed") emit(asm.Opcodes.NOP) @@ -3084,7 +3075,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { assert(nonICode.hasNext, "empty block") nonICode.next.isInstanceOf[JUMP] } - + /** * Returns the list of instructions in a block that follow all ICode only instructions, * where an ICode only instruction is one that won't make it to the JVM @@ -3101,7 +3092,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { * Returns the target of a block that is "jump only" which is defined * as being a block that consists only of 0 or more instructions that * won't make it to the JVM followed by a JUMP. - * + * * @param b The basic block to examine * @return Some(target) if b is a "jump only" block or None if it's not */ @@ -3150,12 +3141,12 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def rephraseGotos(detour: mutable.Map[BasicBlock, BasicBlock]) { def lookup(b: BasicBlock) = detour.getOrElse(b, b) - + m.code.startBlock = lookup(m.code.startBlock) - + for(eh <- m.exh) eh.setStartBlock(lookup(eh.startBlock)) - + for (b <- m.blocks) { def replaceLastInstruction(i: Instruction) = { if (b.lastInstruction != i) { @@ -3164,18 +3155,18 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { b.replaceInstruction(idxLast, i) } } - + b.lastInstruction match { case JUMP(whereto) => replaceLastInstruction(JUMP(lookup(whereto))) case CJUMP(succ, fail, cond, kind) => replaceLastInstruction(CJUMP(lookup(succ), lookup(fail), cond, kind)) - case CZJUMP(succ, fail, cond, kind) => + case CZJUMP(succ, fail, cond, kind) => replaceLastInstruction(CZJUMP(lookup(succ), lookup(fail), cond, kind)) case SWITCH(tags, labels) => val newLabels = (labels map lookup) replaceLastInstruction(SWITCH(tags, newLabels)) - case _ => () + case _ => () } } } @@ -3203,7 +3194,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // blocks for (key <- detour.keySet) { // we use the Robert Floyd's classic Tortoise and Hare algorithm - @tailrec + @tailrec def findDestination(tortoise: BasicBlock, hare: BasicBlock): BasicBlock = { if (tortoise == hare) // cycle detected, map key to key @@ -3227,7 +3218,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } detour } - + val detour = computeDetour rephraseGotos(detour) @@ -3235,33 +3226,33 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { val (remappings, cycles) = detour partition {case (source, target) => source != target} for ((source, target) <- remappings) { debuglog(s"Will elide jump only block $source because it can be jumped around to get to $target.") - if (m.startBlock == source) debugwarn("startBlock should have been re-wired by now") + if (m.startBlock == source) debugwarn("startBlock should have been re-wired by now") } val sources = remappings.keySet val targets = remappings.values.toSet val intersection = sources intersect targets - + if (intersection.nonEmpty) debugwarn(s"contradiction: we seem to have some source and target overlap in blocks ${intersection.mkString}. Map was ${detour.mkString}") - + for ((source, _) <- cycles) { debuglog(s"Block $source is in a do-nothing infinite loop. Did the user write 'while(true){}'?") } } } - + /** * Removes all blocks that are unreachable in a method using a standard reachability analysis. */ def elimUnreachableBlocks(m: IMethod) { - assert(m.hasCode, "code-less method") - + assert(m.hasCode, "code-less method") + // assume nothing is reachable until we prove it can be reached val reachable = mutable.Set[BasicBlock]() - + // the set of blocks that we know are reachable but have // yet to be marked reachable, initially only the start block val worklist = mutable.Set(m.startBlock) - + while (worklist.nonEmpty) { val block = worklist.head worklist remove block @@ -3271,7 +3262,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // think are unreachable worklist ++= (block.successors filterNot reachable) } - + // exception handlers need to be told not to cover unreachable blocks // and exception handlers that no longer cover any blocks need to be // removed entirely @@ -3282,9 +3273,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { unusedExceptionHandlers += exh } } - + // remove the unusued exception handler references - if (settings.debug.value) + if (settings.debug.value) for (exh <- unusedExceptionHandlers) debuglog(s"eliding exception handler $exh because it does not cover any reachable blocks") m.exh = m.exh filterNot unusedExceptionHandlers diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala deleted file mode 100644 index e66e4eff29..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractOrMissingHandler.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -class AbstractOrMissingHandler[T](onError: String => Unit, value: T) extends PartialFunction[Throwable, T] { - def isDefinedAt(t: Throwable) = t match { - case _: AbstractMethodError => true - case _: NoSuchMethodError => true - case _: MissingRequirementError => true - case _: NoClassDefFoundError => true - case _ => false - } - def apply(t: Throwable) = t match { - case x @ (_: AbstractMethodError | _: NoSuchMethodError | _: NoClassDefFoundError) => - onError(""" - |Failed to initialize compiler: %s. - |This is most often remedied by a full clean and recompile. - |Otherwise, your classpath may continue bytecode compiled by - |different and incompatible versions of scala. - |""".stripMargin.format(x.getClass.getName split '.' last) - ) - x.printStackTrace() - value - case x: MissingRequirementError => - onError(""" - |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 - |** object programatically, settings.usejavacp.value = true.""".stripMargin.format(x.req) - ) - value - } -} - -object AbstractOrMissingHandler { - def apply[T]() = new AbstractOrMissingHandler[T](Console println _, null.asInstanceOf[T]) -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala deleted file mode 100644 index 48890a21c6..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import java.lang.reflect -import util.ScalaClassLoader -import ScalaClassLoader.appLoader -import scala.reflect.NameTransformer._ - -object ByteCode { - /** Until I figure out why I can't get scalap onto the classpath such - * that the compiler will bootstrap, we have to use reflection. - */ - private lazy val DECODER: Option[AnyRef] = - for (clazz <- appLoader.tryToLoadClass[AnyRef]("scala.tools.scalap.Decode$")) yield - clazz.getField(MODULE_INSTANCE_NAME).get(null) - - private def decoderMethod(name: String, args: JClass*): Option[reflect.Method] = { - for (decoder <- DECODER ; m <- Option(decoder.getClass.getMethod(name, args: _*))) yield m - } - - private lazy val aliasMap = { - for (module <- DECODER ; method <- decoderMethod("typeAliases", classOf[String])) yield - method.invoke(module, _: String).asInstanceOf[Option[Map[String, String]]] - } - - /** Scala sig bytes. - */ - def scalaSigBytesForPath(path: String) = - for { - module <- DECODER - method <- decoderMethod("scalaSigAnnotationBytes", classOf[String]) - names <- method.invoke(module, path).asInstanceOf[Option[Array[Byte]]] - } - yield names - - def aliasesForPackage(pkg: String) = aliasMap flatMap (_(pkg)) -} diff --git a/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala b/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala deleted file mode 100644 index 0ab92ab769..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/CommandLine.scala +++ /dev/null @@ -1,13 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Lex Spoon - */ - -package scala.tools.nsc -package interpreter - -/** A command line for the interpreter. - */ -class CommandLine(arguments: List[String], error: String => Unit) extends CompilerCommand(arguments, error) { - override def cmdName = "scala" -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala deleted file mode 100644 index 84a5cb49ae..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import Completion._ - -/** An implementation-agnostic completion interface which makes no - * reference to the jline classes. - */ -trait Completion { - type ExecResult - def resetVerbosity(): Unit - def completer(): ScalaCompleter -} -object NoCompletion extends Completion { - type ExecResult = Nothing - def resetVerbosity() = () - def completer() = NullCompleter -} - -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) - } - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala deleted file mode 100644 index 3dd5d93390..0000000000 --- a/src/compiler/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/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala deleted file mode 100644 index d24ad60974..0000000000 --- a/src/compiler/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/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala b/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala deleted file mode 100644 index 48af261937..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala +++ /dev/null @@ -1,63 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.tools.jline.console.{ ConsoleReader, CursorBuffer } - -trait ConsoleReaderHelper extends ConsoleReader { - def terminal = getTerminal() - def width = terminal.getWidth() - def height = terminal.getHeight() - - def readOneKey(prompt: String): Int - def eraseLine(): Unit - - private val marginSize = 3 - private def morePrompt = "--More--" - private def emulateMore(): Int = { - val key = readOneKey(morePrompt) - try key match { - case '\r' | '\n' => 1 - case 'q' => -1 - case _ => height - 1 - } - finally { - eraseLine() - // TODO: still not quite managing to erase --More-- and get - // back to a scala prompt without another keypress. - if (key == 'q') { - putString(getPrompt()) - redrawLine() - flush() - } - } - } - - override def printColumns(items: JCollection[_ <: CharSequence]): Unit = - printColumns(items: List[String]) - - def printColumns(items: List[String]): Unit = { - if (items forall (_ == "")) - return - - val longest = items map (_.length) max - var linesLeft = if (isPaginationEnabled()) height - 1 else Int.MaxValue - val columnSize = longest + marginSize - val padded = items map ("%-" + columnSize + "s" format _) - val groupSize = 1 max (width / columnSize) // make sure it doesn't divide to 0 - - padded grouped groupSize foreach { xs => - println(xs.mkString) - linesLeft -= 1 - if (linesLeft <= 0) { - linesLeft = emulateMore() - if (linesLeft < 0) - return - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Delimited.scala b/src/compiler/scala/tools/nsc/interpreter/Delimited.scala deleted file mode 100644 index e88a044931..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Delimited.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.tools.jline.console.completer.ArgumentCompleter.{ ArgumentDelimiter, ArgumentList } - -class JLineDelimiter extends ArgumentDelimiter { - def toJLine(args: List[String], cursor: Int) = args match { - case Nil => new ArgumentList(new Array[String](0), 0, 0, cursor) - case xs => new ArgumentList(xs.toArray, xs.size - 1, xs.last.length, cursor) - } - - def delimit(buffer: CharSequence, cursor: Int) = { - val p = Parsed(buffer.toString, cursor) - toJLine(p.args, cursor) - } - def isDelimiter(buffer: CharSequence, cursor: Int) = Parsed(buffer.toString, cursor).isDelimiter -} - -trait Delimited { - self: Parsed => - - def delimited: Char => Boolean - def escapeChars: List[Char] = List('\\') - - /** Break String into args based on delimiting function. - */ - protected def toArgs(s: String): List[String] = - if (s == "") Nil - else (s indexWhere isDelimiterChar) match { - case -1 => List(s) - case idx => (s take idx) :: toArgs(s drop (idx + 1)) - } - - def isDelimiterChar(ch: Char) = delimited(ch) - def isEscapeChar(ch: Char): Boolean = escapeChars contains ch -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala deleted file mode 100644 index 9edd54b939..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala +++ /dev/null @@ -1,99 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.tools.nsc.ast.parser.Tokens.EOF - -trait ExprTyper { - val repl: IMain - - import repl._ - import global.{ reporter => _, Import => _, _ } - import definitions._ - import syntaxAnalyzer.UnitParser - import naming.freshInternalVarName - - object codeParser { - val global: repl.global.type = repl.global - def applyRule[T](code: String, rule: UnitParser => T): T = { - reporter.reset() - val scanner = newUnitParser(code) - val result = rule(scanner) - - if (!reporter.hasErrors) - scanner.accept(EOF) - - result - } - def stmts(code: String) = applyRule(code, _.templateStats()) - } - - /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */ - def parse(line: String): Option[List[Tree]] = debugging(s"""parse("$line")""") { - var isIncomplete = false - reporter.withIncompleteHandler((_, _) => isIncomplete = true) { - val trees = codeParser.stmts(line) - if (reporter.hasErrors) Some(Nil) - else if (isIncomplete) None - else Some(trees) - } - } - - def symbolOfLine(code: String): Symbol = { - def asExpr(): Symbol = { - val name = freshInternalVarName() - // Typing it with a lazy val would give us the right type, but runs - // into compiler bugs with things like existentials, so we compile it - // behind a def and strip the NullaryMethodType which wraps the expr. - val line = "def " + name + " = " + code - - interpretSynthetic(line) match { - case IR.Success => - val sym0 = symbolOfTerm(name) - // drop NullaryMethodType - sym0.cloneSymbol setInfo exitingTyper(sym0.info.finalResultType) - case _ => NoSymbol - } - } - def asDefn(): Symbol = { - val old = repl.definedSymbolList.toSet - - interpretSynthetic(code) match { - case IR.Success => - repl.definedSymbolList filterNot old match { - case Nil => NoSymbol - case sym :: Nil => sym - case syms => NoSymbol.newOverloaded(NoPrefix, syms) - } - case _ => NoSymbol - } - } - def asError(): Symbol = { - interpretSynthetic(code) - NoSymbol - } - beSilentDuring(asExpr()) orElse beSilentDuring(asDefn()) orElse asError() - } - - private var typeOfExpressionDepth = 0 - def typeOfExpression(expr: String, silent: Boolean = true): Type = { - if (typeOfExpressionDepth > 2) { - repldbg("Terminating typeOfExpression recursion for expression: " + expr) - return NoType - } - typeOfExpressionDepth += 1 - // Don't presently have a good way to suppress undesirable success output - // while letting errors through, so it is first trying it silently: if there - // is an error, and errors are desired, then it re-evaluates non-silently - // to induce the error message. - try beSilentDuring(symbolOfLine(expr).tpe) match { - case NoType if !silent => symbolOfLine(expr).tpe // generate error - case tpe => tpe - } - finally typeOfExpressionDepth -= 1 - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Formatting.scala b/src/compiler/scala/tools/nsc/interpreter/Formatting.scala deleted file mode 100644 index 43e653edfd..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Formatting.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import util.stringFromWriter - -trait Formatting { - def prompt: String - - def spaces(code: String): String = { - /** Heuristic to avoid indenting and thereby corrupting """-strings and XML literals. */ - val tokens = List("\"\"\"", "</", "/>") - val noIndent = (code contains "\n") && (tokens exists code.contains) - - if (noIndent) "" - else prompt drop 1 map (_ => ' ') - } - /** Indent some code by the width of the scala> prompt. - * This way, compiler error messages read better. - */ - def indentCode(code: String) = { - val indent = spaces(code) - stringFromWriter(str => - for (line <- code.lines) { - str print indent - str print (line + "\n") - str.flush() - } - ) - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala deleted file mode 100644 index 2ea255319d..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ /dev/null @@ -1,749 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Alexander Spoon - */ - -package scala.tools.nsc -package interpreter - -import Predef.{ println => _, _ } -import java.io.{ BufferedReader, FileReader } -import session._ -import scala.annotation.tailrec -import scala.util.Properties.{ jdkHome, javaVersion, versionString, javaVmName } -import scala.tools.util.{ Javap } -import util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream } -import io.{ File, Directory } -import util.ScalaClassLoader -import ScalaClassLoader._ -import scala.tools.util._ -import scala.language.{implicitConversions, existentials} -import scala.reflect.classTag -import scala.tools.reflect.StdRuntimeTags._ -import scala.concurrent.{ ExecutionContext, Await, Future, future } -import ExecutionContext.Implicits._ - -/** The Scala interactive shell. It provides a read-eval-print loop - * around the Interpreter class. - * After instantiation, clients should call the main() method. - * - * If no in0 is specified, then input will come from the console, and - * the class will attempt to provide input editing feature such as - * input history. - * - * @author Moez A. Abdel-Gawad - * @author Lex Spoon - * @version 1.2 - */ -class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) - extends AnyRef - with LoopCommands -{ - def this(in0: BufferedReader, out: JPrintWriter) = this(Some(in0), out) - def this() = this(None, new JPrintWriter(Console.out, true)) - - @deprecated("Use `intp` instead.", "2.9.0") def interpreter = intp - @deprecated("Use `intp` instead.", "2.9.0") def interpreter_= (i: Interpreter): Unit = intp = i - - var in: InteractiveReader = _ // the input stream from which commands come - var settings: Settings = _ - var intp: IMain = _ - - private var globalFuture: Future[Boolean] = _ - - /** Print a welcome message */ - def printWelcome() { - echo(s""" - |Welcome to Scala $versionString ($javaVmName, Java $javaVersion). - |Type in expressions to have them evaluated. - |Type :help for more information.""".trim.stripMargin - ) - replinfo("[info] started at " + new java.util.Date) - } - - protected def asyncMessage(msg: String) { - if (isReplInfo || isReplPower) - echoAndRefresh(msg) - } - - override def echoCommandMessage(msg: String) { - intp.reporter printUntruncatedMessage msg - } - - lazy val power = new Power(intp, new StdReplVals(this))(tagOfStdReplVals, classTag[StdReplVals]) - def history = in.history - - // classpath entries added via :cp - var addedClasspath: String = "" - - /** A reverse list of commands to replay if the user requests a :replay */ - var replayCommandStack: List[String] = Nil - - /** A list of commands to replay if the user requests a :replay */ - def replayCommands = replayCommandStack.reverse - - /** Record a command for replay should the user request a :replay */ - def addReplay(cmd: String) = replayCommandStack ::= cmd - - def savingReplayStack[T](body: => T): T = { - val saved = replayCommandStack - try body - finally replayCommandStack = saved - } - def savingReader[T](body: => T): T = { - val saved = in - try body - finally in = saved - } - - /** Close the interpreter and set the var to null. */ - def closeInterpreter() { - if (intp ne null) { - intp.close() - intp = null - } - } - - class ILoopInterpreter extends IMain(settings, out) { - outer => - - override lazy val formatting = new Formatting { - def prompt = ILoop.this.prompt - } - override protected def parentClassLoader = - settings.explicitParentLoader.getOrElse( classOf[ILoop].getClassLoader ) - } - - /** Create a new interpreter. */ - def createInterpreter() { - if (addedClasspath != "") - settings.classpath append addedClasspath - - intp = new ILoopInterpreter - } - - /** print a friendly help message */ - def helpCommand(line: String): Result = { - if (line == "") helpSummary() - else uniqueCommand(line) match { - case Some(lc) => echo("\n" + lc.help) - case _ => ambiguousError(line) - } - } - private def helpSummary() = { - val usageWidth = commands map (_.usageMsg.length) max - val formatStr = "%-" + usageWidth + "s %s" - - echo("All commands can be abbreviated, e.g. :he instead of :help.") - - commands foreach { cmd => - echo(formatStr.format(cmd.usageMsg, cmd.help)) - } - } - private def ambiguousError(cmd: String): Result = { - matchingCommands(cmd) match { - case Nil => echo(cmd + ": no such command. Type :help for help.") - case xs => echo(cmd + " is ambiguous: did you mean " + xs.map(":" + _.name).mkString(" or ") + "?") - } - Result(keepRunning = true, None) - } - private def matchingCommands(cmd: String) = commands filter (_.name startsWith cmd) - private def uniqueCommand(cmd: String): Option[LoopCommand] = { - // this lets us add commands willy-nilly and only requires enough command to disambiguate - matchingCommands(cmd) match { - case List(x) => Some(x) - // exact match OK even if otherwise appears ambiguous - case xs => xs find (_.name == cmd) - } - } - - /** Show the history */ - lazy val historyCommand = new LoopCommand("history", "show the history (optional num is commands to show)") { - override def usage = "[num]" - def defaultLines = 20 - - def apply(line: String): Result = { - if (history eq NoHistory) - return "No history available." - - val xs = words(line) - val current = history.index - val count = try xs.head.toInt catch { case _: Exception => defaultLines } - val lines = history.asStrings takeRight count - val offset = current - lines.size + 1 - - for ((line, index) <- lines.zipWithIndex) - echo("%3d %s".format(index + offset, line)) - } - } - - // When you know you are most likely breaking into the middle - // of a line being typed. This softens the blow. - protected def echoAndRefresh(msg: String) = { - echo("\n" + msg) - in.redrawLine() - } - protected def echo(msg: String) = { - out println msg - out.flush() - } - - /** Search the history */ - def searchHistory(_cmdline: String) { - val cmdline = _cmdline.toLowerCase - val offset = history.index - history.size + 1 - - for ((line, index) <- history.asStrings.zipWithIndex ; if line.toLowerCase contains cmdline) - echo("%d %s".format(index + offset, line)) - } - - private val currentPrompt = Properties.shellPromptString - - /** Prompt to print when awaiting input */ - def prompt = currentPrompt - - import LoopCommand.{ cmd, nullary } - - /** Standard commands **/ - lazy val standardCommands = List( - cmd("cp", "<path>", "add a jar or directory to the classpath", addClasspath), - cmd("help", "[command]", "print this summary or command-specific help", helpCommand), - historyCommand, - cmd("h?", "<string>", "search the history", searchHistory), - cmd("imports", "[name name ...]", "show import history, identifying sources of names", importsCommand), - cmd("implicits", "[-v]", "show the implicits in scope", intp.implicitsCommand), - cmd("javap", "<path|class>", "disassemble a file or class name", javapCommand), - cmd("load", "<path>", "load and interpret a Scala file", loadCommand), - nullary("paste", "enter paste mode: all input up to ctrl-D compiled together", pasteCommand), - nullary("power", "enable power user mode", powerCmd), - nullary("quit", "exit the interpreter", () => Result(keepRunning = false, None)), - nullary("replay", "reset execution and replay all previous commands", replay), - nullary("reset", "reset the repl to its initial state, forgetting all session entries", resetCommand), - shCommand, - nullary("silent", "disable/enable automatic printing of results", verbosity), - cmd("type", "[-v] <expr>", "display the type of an expression without evaluating it", typeCommand), - nullary("warnings", "show the suppressed warnings from the most recent line which had any", warningsCommand) - ) - - /** Power user commands */ - lazy val powerCommands: List[LoopCommand] = List( - cmd("phase", "<phase>", "set the implicit phase for power commands", phaseCommand) - ) - - private def importsCommand(line: String): Result = { - val tokens = words(line) - val handlers = intp.languageWildcardHandlers ++ intp.importHandlers - - handlers.filterNot(_.importedSymbols.isEmpty).zipWithIndex foreach { - case (handler, idx) => - val (types, terms) = handler.importedSymbols partition (_.name.isTypeName) - val imps = handler.implicitSymbols - val found = tokens filter (handler importsSymbolNamed _) - val typeMsg = if (types.isEmpty) "" else types.size + " types" - val termMsg = if (terms.isEmpty) "" else terms.size + " terms" - val implicitMsg = if (imps.isEmpty) "" else imps.size + " are implicit" - val foundMsg = if (found.isEmpty) "" else found.mkString(" // imports: ", ", ", "") - val statsMsg = List(typeMsg, termMsg, implicitMsg) filterNot (_ == "") mkString ("(", ", ", ")") - - intp.reporter.printMessage("%2d) %-30s %s%s".format( - idx + 1, - handler.importString, - statsMsg, - foundMsg - )) - } - } - - private def findToolsJar() = { - val jdkPath = Directory(jdkHome) - val jar = jdkPath / "lib" / "tools.jar" toFile - - if (jar isFile) - Some(jar) - else if (jdkPath.isDirectory) - jdkPath.deepFiles find (_.name == "tools.jar") - else None - } - private def addToolsJarToLoader() = { - val cl = findToolsJar() match { - case Some(tools) => ScalaClassLoader.fromURLs(Seq(tools.toURL), intp.classLoader) - case _ => intp.classLoader - } - if (Javap.isAvailable(cl)) { - repldbg(":javap available.") - cl - } - else { - repldbg(":javap unavailable: no tools.jar at " + jdkHome) - intp.classLoader - } - } - - protected def newJavap() = - JavapClass(addToolsJarToLoader(), new IMain.ReplStrippingWriter(intp), Some(intp)) - - private lazy val javap = substituteAndLog[Javap]("javap", NoJavap)(newJavap()) - - // Still todo: modules. - private def typeCommand(line0: String): Result = { - line0.trim match { - case "" => ":type [-v] <expression>" - case s if s startsWith "-v " => intp.typeCommandInternal(s stripPrefix "-v " trim, verbose = true) - case s => intp.typeCommandInternal(s, verbose = false) - } - } - - private def warningsCommand(): Result = { - if (intp.lastWarnings.isEmpty) - "Can't find any cached warnings." - else - intp.lastWarnings foreach { case (pos, msg) => intp.reporter.warning(pos, msg) } - } - - private def javapCommand(line: String): Result = { - if (javap == null) - ":javap unavailable, no tools.jar at %s. Set JDK_HOME.".format(jdkHome) - else if (line == "") - ":javap [-lcsvp] [path1 path2 ...]" - else - javap(words(line)) foreach { res => - if (res.isError) return "Failed: " + res.value - else res.show() - } - } - - private def pathToPhaseWrapper = intp.originalPath("$r") + ".phased.atCurrent" - - private def phaseCommand(name: String): Result = { - val phased: Phased = power.phased - import phased.NoPhaseName - - if (name == "clear") { - phased.set(NoPhaseName) - intp.clearExecutionWrapper() - "Cleared active phase." - } - else if (name == "") phased.get match { - case NoPhaseName => "Usage: :phase <expr> (e.g. typer, erasure.next, erasure+3)" - case ph => "Active phase is '%s'. (To clear, :phase clear)".format(phased.get) - } - else { - val what = phased.parse(name) - if (what.isEmpty || !phased.set(what)) - "'" + name + "' does not appear to represent a valid phase." - else { - intp.setExecutionWrapper(pathToPhaseWrapper) - val activeMessage = - if (what.toString.length == name.length) "" + what - else "%s (%s)".format(what, name) - - "Active phase is now: " + activeMessage - } - } - } - - /** Available commands */ - def commands: List[LoopCommand] = standardCommands ++ ( - if (isReplPower) powerCommands else Nil - ) - - val replayQuestionMessage = - """|That entry seems to have slain the compiler. Shall I replay - |your session? I can re-run each line except the last one. - |[y/n] - """.trim.stripMargin - - private val crashRecovery: PartialFunction[Throwable, Boolean] = { - case ex: Throwable => - echo(intp.global.throwableAsString(ex)) - - ex match { - case _: NoSuchMethodError | _: NoClassDefFoundError => - echo("\nUnrecoverable error.") - throw ex - case _ => - def fn(): Boolean = - try in.readYesOrNo(replayQuestionMessage, { echo("\nYou must enter y or n.") ; fn() }) - catch { case _: RuntimeException => false } - - if (fn()) replay() - else echo("\nAbandoning crashed session.") - } - true - } - - // return false if repl should exit - def processLine(line: String): Boolean = { - import scala.concurrent.duration._ - Await.ready(globalFuture, 60.seconds) - - (line ne null) && (command(line) match { - case Result(false, _) => false - case Result(_, Some(line)) => addReplay(line) ; true - case _ => true - }) - } - - private def readOneLine() = { - out.flush() - in readLine prompt - } - - /** The main read-eval-print loop for the repl. It calls - * command() for each line of input, and stops when - * command() returns false. - */ - @tailrec final def loop() { - if ( try processLine(readOneLine()) catch crashRecovery ) - loop() - } - - /** interpret all lines from a specified file */ - def interpretAllFrom(file: File) { - savingReader { - savingReplayStack { - file applyReader { reader => - in = SimpleReader(reader, out, interactive = false) - echo("Loading " + file + "...") - loop() - } - } - } - } - - /** create a new interpreter and replay the given commands */ - def replay() { - reset() - if (replayCommandStack.isEmpty) - echo("Nothing to replay.") - else for (cmd <- replayCommands) { - echo("Replaying: " + cmd) // flush because maybe cmd will have its own output - command(cmd) - echo("") - } - } - def resetCommand() { - echo("Resetting interpreter state.") - if (replayCommandStack.nonEmpty) { - echo("Forgetting this session history:\n") - replayCommands foreach echo - echo("") - replayCommandStack = Nil - } - if (intp.namedDefinedTerms.nonEmpty) - echo("Forgetting all expression results and named terms: " + intp.namedDefinedTerms.mkString(", ")) - if (intp.definedTypes.nonEmpty) - echo("Forgetting defined types: " + intp.definedTypes.mkString(", ")) - - reset() - } - def reset() { - intp.reset() - unleashAndSetPhase() - } - - /** fork a shell and run a command */ - lazy val shCommand = new LoopCommand("sh", "run a shell command (result is implicitly => List[String])") { - override def usage = "<command line>" - def apply(line: String): Result = line match { - case "" => showUsage() - case _ => - val toRun = classOf[ProcessResult].getName + "(" + string2codeQuoted(line) + ")" - intp interpret toRun - () - } - } - - def withFile(filename: String)(action: File => Unit) { - val f = File(filename) - - if (f.exists) action(f) - else echo("That file does not exist") - } - - def loadCommand(arg: String) = { - var shouldReplay: Option[String] = None - withFile(arg)(f => { - interpretAllFrom(f) - shouldReplay = Some(":load " + arg) - }) - Result(keepRunning = true, shouldReplay) - } - - def addClasspath(arg: String): Unit = { - val f = File(arg).normalize - if (f.exists) { - addedClasspath = ClassPath.join(addedClasspath, f.path) - val totalClasspath = ClassPath.join(settings.classpath.value, addedClasspath) - echo("Added '%s'. Your new classpath is:\n\"%s\"".format(f.path, totalClasspath)) - replay() - } - else echo("The path '" + f + "' doesn't seem to exist.") - } - - def powerCmd(): Result = { - if (isReplPower) "Already in power mode." - else enablePowerMode(isDuringInit = false) - } - def enablePowerMode(isDuringInit: Boolean) = { - replProps.power setValue true - unleashAndSetPhase() - asyncEcho(isDuringInit, power.banner) - } - private def unleashAndSetPhase() { - if (isReplPower) { - power.unleash() - // Set the phase to "typer" - intp beSilentDuring phaseCommand("typer") - } - } - - def asyncEcho(async: Boolean, msg: => String) { - if (async) asyncMessage(msg) - else echo(msg) - } - - def verbosity() = { - val old = intp.printResults - intp.printResults = !old - echo("Switched " + (if (old) "off" else "on") + " result printing.") - } - - /** Run one command submitted by the user. Two values are returned: - * (1) whether to keep running, (2) the line to record for replay, - * if any. */ - def command(line: String): Result = { - if (line startsWith ":") { - val cmd = line.tail takeWhile (x => !x.isWhitespace) - uniqueCommand(cmd) match { - case Some(lc) => lc(line.tail stripPrefix cmd dropWhile (_.isWhitespace)) - case _ => ambiguousError(cmd) - } - } - else if (intp.global == null) Result(keepRunning = false, None) // Notice failure to create compiler - else Result(keepRunning = true, interpretStartingWith(line)) - } - - private def readWhile(cond: String => Boolean) = { - Iterator continually in.readLine("") takeWhile (x => x != null && cond(x)) - } - - def pasteCommand(): Result = { - echo("// Entering paste mode (ctrl-D to finish)\n") - val code = readWhile(_ => true) mkString "\n" - echo("\n// Exiting paste mode, now interpreting.\n") - intp interpret code - () - } - - private object paste extends Pasted { - val ContinueString = " | " - val PromptString = "scala> " - - def interpret(line: String): Unit = { - echo(line.trim) - intp interpret line - echo("") - } - - def transcript(start: String) = { - echo("\n// Detected repl transcript paste: ctrl-D to finish.\n") - apply(Iterator(start) ++ readWhile(_.trim != PromptString.trim)) - } - } - import paste.{ ContinueString, PromptString } - - /** Interpret expressions starting with the first line. - * Read lines until a complete compilation unit is available - * or until a syntax error has been seen. If a full unit is - * read, go ahead and interpret it. Return the full string - * to be recorded for replay, if any. - */ - def interpretStartingWith(code: String): Option[String] = { - // signal completion non-completion input has been received - in.completion.resetVerbosity() - - def reallyInterpret = { - val reallyResult = intp.interpret(code) - (reallyResult, reallyResult match { - case IR.Error => None - case IR.Success => Some(code) - case IR.Incomplete => - if (in.interactive && code.endsWith("\n\n")) { - echo("You typed two blank lines. Starting a new command.") - None - } - else in.readLine(ContinueString) match { - case null => - // we know compilation is going to fail since we're at EOF and the - // parser thinks the input is still incomplete, but since this is - // a file being read non-interactively we want to fail. So we send - // it straight to the compiler for the nice error message. - intp.compileString(code) - None - - case line => interpretStartingWith(code + "\n" + line) - } - }) - } - - /** Here we place ourselves between the user and the interpreter and examine - * the input they are ostensibly submitting. We intervene in several cases: - * - * 1) If the line starts with "scala> " it is assumed to be an interpreter paste. - * 2) If the line starts with "." (but not ".." or "./") it is treated as an invocation - * on the previous result. - * 3) If the Completion object's execute returns Some(_), we inject that value - * and avoid the interpreter, as it's likely not valid scala code. - */ - if (code == "") None - else if (!paste.running && code.trim.startsWith(PromptString)) { - paste.transcript(code) - None - } - else if (Completion.looksLikeInvocation(code) && intp.mostRecentVar != "") { - interpretStartingWith(intp.mostRecentVar + code) - } - else if (code.trim startsWith "//") { - // line comment, do nothing - None - } - else - reallyInterpret._2 - } - - // runs :load `file` on any files passed via -i - def loadFiles(settings: Settings) = settings match { - case settings: GenericRunnerSettings => - for (filename <- settings.loadfiles.value) { - val cmd = ":load " + filename - command(cmd) - addReplay(cmd) - echo("") - } - case _ => - } - - /** Tries to create a JLineReader, falling back to SimpleReader: - * unless settings or properties are such that it should start - * with SimpleReader. - */ - def chooseReader(settings: Settings): InteractiveReader = { - if (settings.Xnojline.value || Properties.isEmacsShell) - SimpleReader() - else try new JLineReader( - if (settings.noCompletion.value) NoCompletion - else new JLineCompletion(intp) - ) - catch { - case ex @ (_: Exception | _: NoClassDefFoundError) => - echo("Failed to created JLineReader: " + ex + "\nFalling back to SimpleReader.") - SimpleReader() - } - } - - private def loopPostInit() { - in match { - case x: JLineReader => x.consoleReader.postInit - case _ => - } - // Bind intp somewhere out of the regular namespace where - // we can get at it in generated code. - intp.quietBind(NamedParam[IMain]("$intp", intp)(tagOfIMain, classTag[IMain])) - // Auto-run code via some setting. - ( replProps.replAutorunCode.option - flatMap (f => io.File(f).safeSlurp()) - foreach (intp quietRun _) - ) - // classloader and power mode setup - intp.setContextClassLoader() - if (isReplPower) { - replProps.power setValue true - unleashAndSetPhase() - asyncMessage(power.banner) - } - } - def process(settings: Settings): Boolean = savingContextLoader { - this.settings = settings - createInterpreter() - - // sets in to some kind of reader depending on environmental cues - in = in0.fold(chooseReader(settings))(r => SimpleReader(r, out, interactive = true)) - globalFuture = future { - intp.initializeSynchronous() - loopPostInit() - loadFiles(settings) - !intp.reporter.hasErrors - } - printWelcome() - - try loop() - catch AbstractOrMissingHandler() - finally closeInterpreter() - - true - } - - @deprecated("Use `process` instead", "2.9.0") - def main(settings: Settings): Unit = process(settings) //used by sbt -} - -object ILoop { - implicit def loopToInterpreter(repl: ILoop): IMain = repl.intp - - // Designed primarily for use by test code: take a String with a - // bunch of code, and prints out a transcript of what it would look - // like if you'd just typed it into the repl. - def runForTranscript(code: String, settings: Settings): String = { - import java.io.{ BufferedReader, StringReader, OutputStreamWriter } - - stringFromStream { ostream => - Console.withOut(ostream) { - val output = new JPrintWriter(new OutputStreamWriter(ostream), true) { - override def write(str: String) = { - // completely skip continuation lines - if (str forall (ch => ch.isWhitespace || ch == '|')) () - // print a newline on empty scala prompts - else if ((str contains '\n') && (str.trim == "scala> ")) super.write("\n") - else super.write(str) - } - } - val input = new BufferedReader(new StringReader(code)) { - override def readLine(): String = { - val s = super.readLine() - // helping out by printing the line being interpreted. - if (s != null) - output.println(s) - s - } - } - val repl = new ILoop(input, output) - if (settings.classpath.isDefault) - settings.classpath.value = sys.props("java.class.path") - - repl process settings - } - } - } - - /** Creates an interpreter loop with default settings and feeds - * the given code to it as input. - */ - def run(code: String, sets: Settings = new Settings): String = { - import java.io.{ BufferedReader, StringReader, OutputStreamWriter } - - stringFromStream { ostream => - Console.withOut(ostream) { - val input = new BufferedReader(new StringReader(code)) - val output = new JPrintWriter(new OutputStreamWriter(ostream), true) - val repl = new ILoop(input, output) - - if (sets.classpath.isDefault) - sets.classpath.value = sys.props("java.class.path") - - repl process sets - } - } - } - def run(lines: List[String]): String = run(lines map (_ + "\n") mkString) -} diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala deleted file mode 100644 index c54b01dbb0..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ /dev/null @@ -1,1121 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package interpreter - -import Predef.{ println => _, _ } -import util.stringFromWriter -import scala.reflect.internal.util._ -import java.net.URL -import scala.sys.BooleanProp -import scala.tools.nsc.io.AbstractFile -import reporters._ -import scala.tools.util.PathResolver -import scala.tools.nsc.util.ScalaClassLoader -import ScalaClassLoader.URLClassLoader -import scala.tools.nsc.util.Exceptional.unwrap -import scala.collection.{ mutable, immutable } -import IMain._ -import java.util.concurrent.Future -import scala.reflect.runtime.{ universe => ru } -import scala.reflect.{ ClassTag, classTag } -import scala.tools.reflect.StdRuntimeTags._ - -/** An interpreter for Scala code. - * - * The main public entry points are compile(), interpret(), and bind(). - * The compile() method loads a complete Scala file. The interpret() method - * executes one line of Scala code at the request of the user. The bind() - * method binds an object to a variable that can then be used by later - * interpreted code. - * - * The overall approach is based on compiling the requested code and then - * using a Java classloader and Java reflection to run the code - * and access its results. - * - * In more detail, a single compiler instance is used - * to accumulate all successfully compiled or interpreted Scala code. To - * "interpret" a line of code, the compiler generates a fresh object that - * includes the line of code and which has public member(s) to export - * all variables defined by that code. To extract the result of an - * interpreted line to show the user, a second "result object" is created - * which imports the variables exported by the above object and then - * exports members called "$eval" and "$print". To accomodate user expressions - * that read from variables or methods defined in previous statements, "import" - * statements are used. - * - * This interpreter shares the strengths and weaknesses of using the - * full compiler-to-Java. The main strength is that interpreted code - * behaves exactly as does compiled code, including running at full speed. - * The main weakness is that redefining classes and methods is not handled - * properly, because rebinding at the Java level is technically difficult. - * - * @author Moez A. Abdel-Gawad - * @author Lex Spoon - */ -class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends Imports { - imain => - - object replOutput extends ReplOutput(settings.Yreploutdir) { } - - @deprecated("Use replOutput.dir instead", "2.11.0") - def virtualDirectory = replOutput.dir - // Used in a test case. - def showDirectory() = replOutput.show(out) - - private[nsc] var printResults = true // whether to print result lines - private[nsc] var totalSilence = false // whether to print anything - private var _initializeComplete = false // compiler is initialized - private var _isInitialized: Future[Boolean] = null // set up initialization future - private var bindExceptions = true // whether to bind the lastException variable - private var _executionWrapper = "" // code to be wrapped around all lines - - /** We're going to go to some trouble to initialize the compiler asynchronously. - * It's critical that nothing call into it until it's been initialized or we will - * run into unrecoverable issues, but the perceived repl startup time goes - * through the roof if we wait for it. So we initialize it with a future and - * use a lazy val to ensure that any attempt to use the compiler object waits - * on the future. - */ - private var _classLoader: AbstractFileClassLoader = null // active classloader - private val _compiler: Global = newCompiler(settings, reporter) // our private compiler - - def compilerClasspath: Seq[URL] = ( - if (isInitializeComplete) global.classPath.asURLs - else new PathResolver(settings).result.asURLs // the compiler's classpath - ) - def settings = initialSettings - // Run the code body with the given boolean settings flipped to true. - def withoutWarnings[T](body: => T): T = beQuietDuring { - val saved = settings.nowarn.value - if (!saved) - settings.nowarn.value = true - - try body - finally if (!saved) settings.nowarn.value = false - } - - /** construct an interpreter that reports to Console */ - def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true)) - def this() = this(new Settings()) - - lazy val formatting: Formatting = new Formatting { - val prompt = Properties.shellPromptString - } - lazy val reporter: ReplReporter = new ReplReporter(this) - - import formatting._ - import reporter.{ printMessage, withoutTruncating } - - // This exists mostly because using the reporter too early leads to deadlock. - private def echo(msg: String) { Console println msg } - private def _initSources = List(new BatchSourceFile("<init>", "class $repl_$init { }")) - private def _initialize() = { - try { - // todo. if this crashes, REPL will hang - new _compiler.Run() compileSources _initSources - _initializeComplete = true - true - } - catch AbstractOrMissingHandler() - } - private def tquoted(s: String) = "\"\"\"" + s + "\"\"\"" - private val logScope = scala.sys.props contains "scala.repl.scope" - private def scopelog(msg: String) = if (logScope) Console.err.println(msg) - - // argument is a thunk to execute after init is done - def initialize(postInitSignal: => Unit) { - synchronized { - if (_isInitialized == null) { - _isInitialized = io.spawn { - try _initialize() - finally postInitSignal - } - } - } - } - def initializeSynchronous(): Unit = { - if (!isInitializeComplete) { - _initialize() - assert(global != null, global) - } - } - def isInitializeComplete = _initializeComplete - - /** the public, go through the future compiler */ - lazy val global: Global = { - if (isInitializeComplete) _compiler - else { - // If init hasn't been called yet you're on your own. - if (_isInitialized == null) { - repldbg("Warning: compiler accessed before init set up. Assuming no postInit code.") - initialize(()) - } - // blocks until it is ; false means catastrophic failure - if (_isInitialized.get()) _compiler - else null - } - } - - import global._ - import definitions.{ ObjectClass, termMember, dropNullaryMethod} - - lazy val runtimeMirror = ru.runtimeMirror(classLoader) - - private def noFatal(body: => Symbol): Symbol = try body catch { case _: FatalError => NoSymbol } - - def getClassIfDefined(path: String) = ( - noFatal(runtimeMirror staticClass path) - orElse noFatal(rootMirror staticClass path) - ) - def getModuleIfDefined(path: String) = ( - noFatal(runtimeMirror staticModule path) - orElse noFatal(rootMirror staticModule path) - ) - - implicit class ReplTypeOps(tp: Type) { - def andAlso(fn: Type => Type): Type = if (tp eq NoType) tp else fn(tp) - } - - // TODO: If we try to make naming a lazy val, we run into big time - // scalac unhappiness with what look like cycles. It has not been easy to - // reduce, but name resolution clearly takes different paths. - object naming extends { - val global: imain.global.type = imain.global - } with Naming { - // make sure we don't overwrite their unwisely named res3 etc. - def freshUserTermName(): TermName = { - val name = newTermName(freshUserVarName()) - if (replScope containsName name) freshUserTermName() - else name - } - def isInternalTermName(name: Name) = isInternalVarName("" + name) - } - import naming._ - - object deconstruct extends { - val global: imain.global.type = imain.global - } with StructuredTypeStrings - - lazy val memberHandlers = new { - val intp: imain.type = imain - } with MemberHandlers - import memberHandlers._ - - /** Temporarily be quiet */ - def beQuietDuring[T](body: => T): T = { - val saved = printResults - printResults = false - try body - finally printResults = saved - } - def beSilentDuring[T](operation: => T): T = { - val saved = totalSilence - totalSilence = true - try operation - finally totalSilence = saved - } - - def quietRun[T](code: String) = beQuietDuring(interpret(code)) - - /** takes AnyRef because it may be binding a Throwable or an Exceptional */ - private def withLastExceptionLock[T](body: => T, alt: => T): T = { - assert(bindExceptions, "withLastExceptionLock called incorrectly.") - bindExceptions = false - - try beQuietDuring(body) - catch logAndDiscard("withLastExceptionLock", alt) - finally bindExceptions = true - } - - def executionWrapper = _executionWrapper - def setExecutionWrapper(code: String) = _executionWrapper = code - def clearExecutionWrapper() = _executionWrapper = "" - - /** interpreter settings */ - lazy val isettings = new ISettings(this) - - /** Instantiate a compiler. Overridable. */ - protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = { - settings.outputDirs setSingleOutput replOutput.dir - settings.exposeEmptyPackage.value = true - new Global(settings, reporter) with ReplGlobal { override def toString: String = "<global>" } - } - - /** Parent classloader. Overridable. */ - protected def parentClassLoader: ClassLoader = - settings.explicitParentLoader.getOrElse( this.getClass.getClassLoader() ) - - /* A single class loader is used for all commands interpreted by this Interpreter. - It would also be possible to create a new class loader for each command - to interpret. The advantages of the current approach are: - - - Expressions are only evaluated one time. This is especially - significant for I/O, e.g. "val x = Console.readLine" - - The main disadvantage is: - - - Objects, classes, and methods cannot be rebound. Instead, definitions - shadow the old ones, and old code objects refer to the old - definitions. - */ - def resetClassLoader() = { - repldbg("Setting new classloader: was " + _classLoader) - _classLoader = null - ensureClassLoader() - } - final def ensureClassLoader() { - if (_classLoader == null) - _classLoader = makeClassLoader() - } - def classLoader: AbstractFileClassLoader = { - ensureClassLoader() - _classLoader - } - - def backticked(s: String): String = ( - (s split '.').toList map { - case "_" => "_" - case s if nme.keywords(newTermName(s)) => s"`$s`" - case s => s - } mkString "." - ) - - abstract class PhaseDependentOps { - def shift[T](op: => T): T - - def path(name: => Name): String = shift(path(symbolOfName(name))) - def path(sym: Symbol): String = backticked(shift(sym.fullName)) - def sig(sym: Symbol): String = shift(sym.defString) - } - object typerOp extends PhaseDependentOps { - def shift[T](op: => T): T = exitingTyper(op) - } - object flatOp extends PhaseDependentOps { - def shift[T](op: => T): T = exitingFlatten(op) - } - - def originalPath(name: String): String = originalPath(name: TermName) - def originalPath(name: Name): String = typerOp path name - def originalPath(sym: Symbol): String = typerOp path sym - def flatPath(sym: Symbol): String = flatOp shift sym.javaClassName - def translatePath(path: String) = { - val sym = if (path endsWith "$") symbolOfTerm(path.init) else symbolOfIdent(path) - sym match { - case NoSymbol => None - case _ => Some(flatPath(sym)) - } - } - def translateEnclosingClass(n: String) = { - def enclosingClass(s: Symbol): Symbol = - if (s == NoSymbol || s.isClass) s else enclosingClass(s.owner) - enclosingClass(symbolOfTerm(n)) match { - case NoSymbol => None - case c => Some(flatPath(c)) - } - } - - private class TranslatingClassLoader(parent: ClassLoader) extends AbstractFileClassLoader(replOutput.dir, parent) { - /** Overridden here to try translating a simple name to the generated - * class name if the original attempt fails. This method is used by - * getResourceAsStream as well as findClass. - */ - override protected def findAbstractFile(name: String): AbstractFile = - super.findAbstractFile(name) match { - case null => translatePath(name) map (super.findAbstractFile(_)) orNull - case file => file - } - } - private def makeClassLoader(): AbstractFileClassLoader = - new TranslatingClassLoader(parentClassLoader match { - case null => ScalaClassLoader fromURLs compilerClasspath - case p => new URLClassLoader(compilerClasspath, p) - }) - - // Set the current Java "context" class loader to this interpreter's class loader - def setContextClassLoader() = classLoader.setAsContext() - - def allDefinedNames: List[Name] = exitingTyper(replScope.toList.map(_.name).sorted) - def unqualifiedIds: List[String] = allDefinedNames map (_.decode) sorted - - /** Most recent tree handled which wasn't wholly synthetic. */ - private def mostRecentlyHandledTree: Option[Tree] = { - prevRequests.reverse foreach { req => - req.handlers.reverse foreach { - case x: MemberDefHandler if x.definesValue && !isInternalTermName(x.name) => return Some(x.member) - case _ => () - } - } - None - } - - private def updateReplScope(sym: Symbol, isDefined: Boolean) { - def log(what: String) { - val mark = if (sym.isType) "t " else "v " - val name = exitingTyper(sym.nameString) - val info = cleanTypeAfterTyper(sym) - val defn = sym defStringSeenAs info - - scopelog(f"[$mark$what%6s] $name%-25s $defn%s") - } - if (ObjectClass isSubClass sym.owner) return - // unlink previous - replScope lookupAll sym.name foreach { sym => - log("unlink") - replScope unlink sym - } - val what = if (isDefined) "define" else "import" - log(what) - replScope enter sym - } - - def recordRequest(req: Request) { - if (req == null) - return - - prevRequests += req - - // warning about serially defining companions. It'd be easy - // enough to just redefine them together but that may not always - // be what people want so I'm waiting until I can do it better. - exitingTyper { - req.defines filterNot (s => req.defines contains s.companionSymbol) foreach { newSym => - val oldSym = replScope lookup newSym.name.companionName - if (Seq(oldSym, newSym).permutations exists { case Seq(s1, s2) => s1.isClass && s2.isModule }) { - replwarn(s"warning: previously defined $oldSym is not a companion to $newSym.") - replwarn("Companions must be defined together; you may wish to use :paste mode for this.") - } - } - } - exitingTyper { - req.imports foreach (sym => updateReplScope(sym, isDefined = false)) - req.defines foreach (sym => updateReplScope(sym, isDefined = true)) - } - } - - private[nsc] def replwarn(msg: => String) { - if (!settings.nowarnings.value) - printMessage(msg) - } - - def compileSourcesKeepingRun(sources: SourceFile*) = { - val run = new Run() - reporter.reset() - run compileSources sources.toList - (!reporter.hasErrors, run) - } - - /** Compile an nsc SourceFile. Returns true if there are - * no compilation errors, or false otherwise. - */ - def compileSources(sources: SourceFile*): Boolean = - compileSourcesKeepingRun(sources: _*)._1 - - /** Compile a string. Returns true if there are no - * compilation errors, or false otherwise. - */ - def compileString(code: String): Boolean = - compileSources(new BatchSourceFile("<script>", code)) - - /** Build a request from the user. `trees` is `line` after being parsed. - */ - private def buildRequest(line: String, trees: List[Tree]): Request = { - executingRequest = new Request(line, trees) - executingRequest - } - - private def safePos(t: Tree, alt: Int): Int = - try t.pos.startOrPoint - catch { case _: UnsupportedOperationException => alt } - - // Given an expression like 10 * 10 * 10 we receive the parent tree positioned - // at a '*'. So look at each subtree and find the earliest of all positions. - private def earliestPosition(tree: Tree): Int = { - var pos = Int.MaxValue - tree foreach { t => - pos = math.min(pos, safePos(t, Int.MaxValue)) - } - pos - } - - private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = { - val content = indentCode(line) - val trees = parse(content) match { - case None => return Left(IR.Incomplete) - case Some(Nil) => return Left(IR.Error) // parse error or empty input - case Some(trees) => trees - } - repltrace( - trees map (t => { - // [Eugene to Paul] previously it just said `t map ...` - // because there was an implicit conversion from Tree to a list of Trees - // however Martin and I have removed the conversion - // (it was conflicting with the new reflection API), - // so I had to rewrite this a bit - val subs = t collect { case sub => sub } - subs map (t0 => - " " + safePos(t0, -1) + ": " + t0.shortClass + "\n" - ) mkString "" - }) mkString "\n" - ) - // If the last tree is a bare expression, pinpoint where it begins using the - // AST node position and snap the line off there. Rewrite the code embodied - // by the last tree as a ValDef instead, so we can access the value. - trees.last match { - case _:Assign => // we don't want to include assignments - case _:TermTree | _:Ident | _:Select => // ... but do want other unnamed terms. - val varName = if (synthetic) freshInternalVarName() else freshUserVarName() - val rewrittenLine = ( - // In theory this would come out the same without the 1-specific test, but - // it's a cushion against any more sneaky parse-tree position vs. code mismatches: - // this way such issues will only arise on multiple-statement repl input lines, - // which most people don't use. - if (trees.size == 1) "val " + varName + " =\n" + content - else { - // The position of the last tree - val lastpos0 = earliestPosition(trees.last) - // Oh boy, the parser throws away parens so "(2+2)" is mispositioned, - // with increasingly hard to decipher positions as we move on to "() => 5", - // (x: Int) => x + 1, and more. So I abandon attempts to finesse and just - // look for semicolons and newlines, which I'm sure is also buggy. - val (raw1, raw2) = content splitAt lastpos0 - repldbg("[raw] " + raw1 + " <---> " + raw2) - - val adjustment = (raw1.reverse takeWhile (ch => (ch != ';') && (ch != '\n'))).size - val lastpos = lastpos0 - adjustment - - // the source code split at the laboriously determined position. - val (l1, l2) = content splitAt lastpos - repldbg("[adj] " + l1 + " <---> " + l2) - - val prefix = if (l1.trim == "") "" else l1 + ";\n" - // Note to self: val source needs to have this precise structure so that - // error messages print the user-submitted part without the "val res0 = " part. - val combined = prefix + "val " + varName + " =\n" + l2 - - repldbg(List( - " line" -> line, - " content" -> content, - " was" -> l2, - "combined" -> combined) map { - case (label, s) => label + ": '" + s + "'" - } mkString "\n" - ) - combined - } - ) - // Rewriting "foo ; bar ; 123" - // to "foo ; bar ; val resXX = 123" - requestFromLine(rewrittenLine, synthetic) match { - case Right(req) => return Right(req withOriginalLine line) - case x => return x - } - case _ => - } - Right(buildRequest(line, trees)) - } - - // dealias non-public types so we don't see protected aliases like Self - def dealiasNonPublic(tp: Type) = tp match { - case TypeRef(_, sym, _) if sym.isAliasType && !sym.isPublic => tp.dealias - case _ => tp - } - - /** - * Interpret one line of input. All feedback, including parse errors - * and evaluation results, are printed via the supplied compiler's - * reporter. Values defined are available for future interpreted strings. - * - * The return value is whether the line was interpreter successfully, - * e.g. that there were no parse errors. - */ - def interpret(line: String): IR.Result = interpret(line, synthetic = false) - def interpretSynthetic(line: String): IR.Result = interpret(line, synthetic = true) - def interpret(line: String, synthetic: Boolean): IR.Result = { - def loadAndRunReq(req: Request) = { - classLoader.setAsContext() - val (result, succeeded) = req.loadAndRun - - /** 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. - */ - if (succeeded) { - if (printResults && result != "") - printMessage(result stripSuffix "\n") - else if (isReplDebug) // show quiet-mode activity - printMessage(result.trim.lines map ("[quiet] " + _) mkString "\n") - - // Book-keeping. Have to record synthetic requests too, - // as they may have been issued for information, e.g. :type - recordRequest(req) - IR.Success - } - else { - // don't truncate stack traces - withoutTruncating(printMessage(result)) - IR.Error - } - } - - if (global == null) IR.Error - else requestFromLine(line, synthetic) match { - case Left(result) => result - case Right(req) => - // null indicates a disallowed statement type; otherwise compile and - // fail if false (implying e.g. a type error) - if (req == null || !req.compile) IR.Error - else loadAndRunReq(req) - } - } - - /** Bind a specified name to a specified value. The name may - * later be used by expressions passed to interpret. - * - * @param name the variable name to bind - * @param boundType the type of the variable, as a string - * @param value the object value to bind to it - * @return an indication of whether the binding succeeded - */ - def bind(name: String, boundType: String, value: Any, modifiers: List[String] = Nil): IR.Result = { - val bindRep = new ReadEvalPrint() - bindRep.compile(""" - |object %s { - | var value: %s = _ - | def set(x: Any) = value = x.asInstanceOf[%s] - |} - """.stripMargin.format(bindRep.evalName, boundType, boundType) - ) - bindRep.callEither("set", value) match { - case Left(ex) => - repldbg("Set failed in bind(%s, %s, %s)".format(name, boundType, value)) - repldbg(util.stackTraceString(ex)) - IR.Error - - case Right(_) => - val line = "%sval %s = %s.value".format(modifiers map (_ + " ") mkString, name, bindRep.evalPath) - repldbg("Interpreting: " + line) - interpret(line) - } - } - def directBind(name: String, boundType: String, value: Any): IR.Result = { - val result = bind(name, boundType, value) - if (result == IR.Success) - directlyBoundNames += newTermName(name) - result - } - def directBind(p: NamedParam): IR.Result = directBind(p.name, p.tpe, p.value) - def directBind[T: ru.TypeTag : ClassTag](name: String, value: T): IR.Result = directBind((name, value)) - - def rebind(p: NamedParam): IR.Result = { - val name = p.name - val newType = p.tpe - val tempName = freshInternalVarName() - - quietRun("val %s = %s".format(tempName, name)) - quietRun("val %s = %s.asInstanceOf[%s]".format(name, tempName, newType)) - } - def quietBind(p: NamedParam): IR.Result = beQuietDuring(bind(p)) - def bind(p: NamedParam): IR.Result = bind(p.name, p.tpe, p.value) - def bind[T: ru.TypeTag : ClassTag](name: String, value: T): IR.Result = bind((name, value)) - - /** Reset this interpreter, forgetting all user-specified requests. */ - def reset() { - clearExecutionWrapper() - resetClassLoader() - resetAllCreators() - prevRequests.clear() - resetReplScope() - replOutput.dir.clear() - } - - /** This instance is no longer needed, so release any resources - * it is using. The reporter's output gets flushed. - */ - def close() { - reporter.flush() - } - - /** Here is where we: - * - * 1) Read some source code, and put it in the "read" object. - * 2) Evaluate the read object, and put the result in the "eval" object. - * 3) Create a String for human consumption, and put it in the "print" object. - * - * Read! Eval! Print! Some of that not yet centralized here. - */ - class ReadEvalPrint(lineId: Int) { - def this() = this(freshLineId()) - - val packageName = sessionNames.line + lineId - val readName = sessionNames.read - val evalName = sessionNames.eval - val printName = sessionNames.print - val resultName = sessionNames.result - - def bindError(t: Throwable) = { - if (!bindExceptions) // avoid looping if already binding - throw t - - val unwrapped = unwrap(t) - withLastExceptionLock[String]({ - directBind[Throwable]("lastException", unwrapped)(tagOfThrowable, classTag[Throwable]) - util.stackTraceString(unwrapped) - }, util.stackTraceString(unwrapped)) - } - - // TODO: split it out into a package object and a regular - // object and we can do that much less wrapping. - def packageDecl = "package " + packageName - - def pathTo(name: String) = packageName + "." + name - def packaged(code: String) = packageDecl + "\n\n" + code - - def readPath = pathTo(readName) - def evalPath = pathTo(evalName) - - def call(name: String, args: Any*): AnyRef = { - val m = evalMethod(name) - repldbg("Invoking: " + m) - if (args.nonEmpty) - repldbg(" with args: " + args.mkString(", ")) - - m.invoke(evalClass, args.map(_.asInstanceOf[AnyRef]): _*) - } - - def callEither(name: String, args: Any*): Either[Throwable, AnyRef] = - try Right(call(name, args: _*)) - catch { case ex: Throwable => Left(ex) } - - class EvalException(msg: String, cause: Throwable) extends RuntimeException(msg, cause) { } - - private def evalError(path: String, ex: Throwable) = - throw new EvalException("Failed to load '" + path + "': " + ex.getMessage, ex) - - private def load(path: String): Class[_] = { - try Class.forName(path, true, classLoader) - catch { case ex: Throwable => evalError(path, unwrap(ex)) } - } - - lazy val evalClass = load(evalPath) - - def compile(source: String): Boolean = compileAndSaveRun("<console>", source) - - /** The innermost object inside the wrapper, found by - * following accessPath into the outer one. - */ - def resolvePathToSymbol(accessPath: String): Symbol = { - val readRoot = getModuleIfDefined(readPath) // the outermost wrapper - (accessPath split '.').foldLeft(readRoot: Symbol) { - case (sym, "") => sym - case (sym, name) => exitingTyper(termMember(sym, name)) - } - } - /** We get a bunch of repeated warnings for reasons I haven't - * entirely figured out yet. For now, squash. - */ - private def updateRecentWarnings(run: Run) { - def loop(xs: List[(Position, String)]): List[(Position, String)] = xs match { - case Nil => Nil - case ((pos, msg)) :: rest => - val filtered = rest filter { case (pos0, msg0) => - (msg != msg0) || (pos.lineContent.trim != pos0.lineContent.trim) || { - // same messages and same line content after whitespace removal - // but we want to let through multiple warnings on the same line - // from the same run. The untrimmed line will be the same since - // there's no whitespace indenting blowing it. - (pos.lineContent == pos0.lineContent) - } - } - ((pos, msg)) :: loop(filtered) - } - val warnings = loop(run.allConditionalWarnings flatMap (_.warnings)) - if (warnings.nonEmpty) - mostRecentWarnings = warnings - } - private def evalMethod(name: String) = evalClass.getMethods filter (_.getName == name) match { - case Array(method) => method - case xs => sys.error("Internal error: eval object " + evalClass + ", " + xs.mkString("\n", "\n", "")) - } - private def compileAndSaveRun(label: String, code: String) = { - showCodeIfDebugging(code) - val (success, run) = compileSourcesKeepingRun(new BatchSourceFile(label, packaged(code))) - updateRecentWarnings(run) - success - } - } - - /** One line of code submitted by the user for interpretation */ - class Request(val line: String, val trees: List[Tree]) { - def defines = defHandlers flatMap (_.definedSymbols) - def imports = importedSymbols - def value = Some(handlers.last) filter (h => h.definesValue) map (h => definedSymbols(h.definesTerm.get)) getOrElse NoSymbol - - val lineRep = new ReadEvalPrint() - - private var _originalLine: String = null - def withOriginalLine(s: String): this.type = { _originalLine = s ; this } - def originalLine = if (_originalLine == null) line else _originalLine - - /** handlers for each tree in this request */ - val handlers: List[MemberHandler] = trees map (memberHandlers chooseHandler _) - def defHandlers = handlers collect { case x: MemberDefHandler => x } - - /** list of names used by this expression */ - val referencedNames: List[Name] = handlers flatMap (_.referencedNames) - - /** def and val names */ - def termNames = handlers flatMap (_.definesTerm) - def typeNames = handlers flatMap (_.definesType) - def importedSymbols = handlers flatMap { - case x: ImportHandler => x.importedSymbols - case _ => Nil - } - - /** Code to import bound names from previous lines - accessPath is code to - * append to objectName to access anything bound by request. - */ - val ComputedImports(importsPreamble, importsTrailer, accessPath) = - exitingTyper(importsCode(referencedNames.toSet)) - - /** the line of code to compute */ - def toCompute = line - - def fullPath(vname: String) = s"${lineRep.readPath}$accessPath.`$vname`" - - /** generate the source code for the object that computes this request */ - private object ObjectSourceCode extends CodeAssembler[MemberHandler] { - def path = originalPath("$intp") - def envLines = { - if (!isReplPower) Nil // power mode only for now - // $intp is not bound; punt, but include the line. - else if (path == "$intp") List( - "def $line = " + tquoted(originalLine), - "def $trees = Nil" - ) - else List( - "def $line = " + tquoted(originalLine), - "def $trees = Nil" - ) - } - - val preamble = """ - |object %s { - |%s%s%s - """.stripMargin.format(lineRep.readName, envLines.map(" " + _ + ";\n").mkString, importsPreamble, indentCode(toCompute)) - val postamble = importsTrailer + "\n}" - val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this - } - - private object ResultObjectSourceCode extends CodeAssembler[MemberHandler] { - /** We only want to generate this code when the result - * is a value which can be referred to as-is. - */ - val evalResult = Request.this.value match { - case NoSymbol => "" - case sym => "lazy val %s = %s".format(lineRep.resultName, originalPath(sym)) - } - // first line evaluates object to make sure constructor is run - // initial "" so later code can uniformly be: + etc - val preamble = """ - |object %s { - | %s - | val %s: String = %s { - | %s - | ("" - """.stripMargin.format( - lineRep.evalName, evalResult, lineRep.printName, - executionWrapper, lineRep.readName + accessPath - ) - - val postamble = """ - | ) - | } - |} - """.stripMargin - val generate = (m: MemberHandler) => m resultExtractionCode Request.this - } - - /** Compile the object file. Returns whether the compilation succeeded. - * If all goes well, the "types" map is computed. */ - lazy val compile: Boolean = { - // error counting is wrong, hence interpreter may overlook failure - so we reset - reporter.reset() - - // compile the object containing the user's code - lineRep.compile(ObjectSourceCode(handlers)) && { - // extract and remember types - typeOf - typesOfDefinedTerms - - // Assign symbols to the original trees - // TODO - just use the new trees. - defHandlers foreach { dh => - val name = dh.member.name - definedSymbols get name foreach { sym => - dh.member setSymbol sym - repldbg("Set symbol of " + name + " to " + symbolDefString(sym)) - } - } - - // compile the result-extraction object - withoutWarnings(lineRep compile ResultObjectSourceCode(handlers)) - } - } - - lazy val resultSymbol = lineRep.resolvePathToSymbol(accessPath) - def applyToResultMember[T](name: Name, f: Symbol => T) = exitingTyper(f(resultSymbol.info.nonPrivateDecl(name))) - - /* typeOf lookup with encoding */ - def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name.toString))) - - private def typeMap[T](f: Type => T) = - mapFrom[Name, Name, T](termNames ++ typeNames)(x => f(cleanMemberDecl(resultSymbol, x))) - - /** Types of variables defined by this request. */ - lazy val compilerTypeOf = typeMap[Type](x => x) withDefaultValue NoType - /** String representations of same. */ - lazy val typeOf = typeMap[String](tp => exitingTyper(tp.toString)) - - lazy val definedSymbols = ( - termNames.map(x => x -> applyToResultMember(x, x => x)) ++ - typeNames.map(x => x -> compilerTypeOf(x).typeSymbolDirect) - ).toMap[Name, Symbol] withDefaultValue NoSymbol - - lazy val typesOfDefinedTerms = mapFrom[Name, Name, Type](termNames)(x => applyToResultMember(x, _.tpe)) - - /** load and run the code using reflection */ - def loadAndRun: (String, Boolean) = { - try { ("" + (lineRep call sessionNames.print), true) } - catch { case ex: Throwable => (lineRep.bindError(ex), false) } - } - - override def toString = "Request(line=%s, %s trees)".format(line, trees.size) - } - - /** Returns the name of the most recent interpreter result. - * Mostly this exists so you can conveniently invoke methods on - * the previous result. - */ - def mostRecentVar: String = - if (mostRecentlyHandledTree.isEmpty) "" - else "" + (mostRecentlyHandledTree.get match { - case x: ValOrDefDef => x.name - case Assign(Ident(name), _) => name - case ModuleDef(_, name, _) => name - case _ => naming.mostRecentVar - }) - - private var mostRecentWarnings: List[(global.Position, String)] = Nil - def lastWarnings = mostRecentWarnings - - private lazy val importToGlobal = global mkImporter ru - private lazy val importToRuntime = ru mkImporter global - private lazy val javaMirror = ru.rootMirror match { - case x: ru.JavaMirror => x - case _ => null - } - private implicit def importFromRu(sym: ru.Symbol): Symbol = importToGlobal importSymbol sym - private implicit def importToRu(sym: Symbol): ru.Symbol = importToRuntime importSymbol sym - - def classOfTerm(id: String): Option[JClass] = symbolOfTerm(id) match { - case NoSymbol => None - case sym => Some(javaMirror runtimeClass importToRu(sym).asClass) - } - - def typeOfTerm(id: String): Type = symbolOfTerm(id).tpe - - def valueOfTerm(id: String): Option[Any] = exitingTyper { - def value() = { - val sym0 = symbolOfTerm(id) - val sym = (importToRuntime importSymbol sym0).asTerm - val module = runtimeMirror.reflectModule(sym.owner.companionSymbol.asModule).instance - val module1 = runtimeMirror.reflect(module) - val invoker = module1.reflectField(sym) - - invoker.get - } - - try Some(value()) catch { case _: Exception => None } - } - - /** It's a bit of a shotgun approach, but for now we will gain in - * robustness. Try a symbol-producing operation at phase typer, and - * if that is NoSymbol, try again at phase flatten. I'll be able to - * lose this and run only from exitingTyper as soon as I figure out - * exactly where a flat name is sneaking in when calculating imports. - */ - def tryTwice(op: => Symbol): Symbol = exitingTyper(op) orElse exitingFlatten(op) - - def symbolOfIdent(id: String): Symbol = symbolOfType(id) orElse symbolOfTerm(id) - def symbolOfType(id: String): Symbol = tryTwice(replScope lookup (id: TypeName)) - def symbolOfTerm(id: String): Symbol = tryTwice(replScope lookup (id: TermName)) - def symbolOfName(id: Name): Symbol = replScope lookup id - - def runtimeClassAndTypeOfTerm(id: String): Option[(JClass, Type)] = { - classOfTerm(id) flatMap { clazz => - clazz.supers find (!_.isScalaAnonymous) map { nonAnon => - (nonAnon, runtimeTypeOfTerm(id)) - } - } - } - - def runtimeTypeOfTerm(id: String): Type = { - typeOfTerm(id) andAlso { tpe => - val clazz = classOfTerm(id) getOrElse { return NoType } - val staticSym = tpe.typeSymbol - val runtimeSym = getClassIfDefined(clazz.getName) - - if ((runtimeSym != NoSymbol) && (runtimeSym != staticSym) && (runtimeSym isSubClass staticSym)) - runtimeSym.info - else NoType - } - } - - def cleanTypeAfterTyper(sym: => Symbol): Type = { - exitingTyper( - dealiasNonPublic( - dropNullaryMethod( - sym.tpe_* - ) - ) - ) - } - def cleanMemberDecl(owner: Symbol, member: Name): Type = - cleanTypeAfterTyper(owner.info nonPrivateDecl member) - - object exprTyper extends { - val repl: IMain.this.type = imain - } with ExprTyper { } - - def parse(line: String): Option[List[Tree]] = exprTyper.parse(line) - - def symbolOfLine(code: String): Symbol = - exprTyper.symbolOfLine(code) - - def typeOfExpression(expr: String, silent: Boolean = true): Type = - exprTyper.typeOfExpression(expr, silent) - - protected def onlyTerms(xs: List[Name]): List[TermName] = xs collect { case x: TermName => x } - protected def onlyTypes(xs: List[Name]): List[TypeName] = xs collect { case x: TypeName => x } - - def definedTerms = onlyTerms(allDefinedNames) filterNot isInternalTermName - def definedTypes = onlyTypes(allDefinedNames) - def definedSymbolList = prevRequestList flatMap (_.defines) filterNot (s => isInternalTermName(s.name)) - - // Terms with user-given names (i.e. not res0 and not synthetic) - def namedDefinedTerms = definedTerms filterNot (x => isUserVarName("" + x) || directlyBoundNames(x)) - - private var _replScope: Scope = _ - private def resetReplScope() { - _replScope = newScope - } - def replScope = { - if (_replScope eq null) - _replScope = newScope - - _replScope - } - - private var executingRequest: Request = _ - private val prevRequests = mutable.ListBuffer[Request]() - private val directlyBoundNames = mutable.Set[Name]() - - def allHandlers = prevRequestList flatMap (_.handlers) - def lastRequest = if (prevRequests.isEmpty) null else prevRequests.last - def prevRequestList = prevRequests.toList - def importHandlers = allHandlers collect { case x: ImportHandler => x } - - def withoutUnwrapping(op: => Unit): Unit = { - val saved = isettings.unwrapStrings - isettings.unwrapStrings = false - try op - finally isettings.unwrapStrings = saved - } - - def symbolDefString(sym: Symbol) = { - TypeStrings.quieter( - exitingTyper(sym.defString), - sym.owner.name + ".this.", - sym.owner.fullName + "." - ) - } - - def showCodeIfDebugging(code: String) { - /** Secret bookcase entrance for repl debuggers: end the line - * with "// show" and see what's going on. - */ - def isShow = code.lines exists (_.trim endsWith "// show") - if (isReplDebug || isShow) { - beSilentDuring(parse(code)) foreach { ts => - ts foreach { t => - withoutUnwrapping(echo(asCompactString(t))) - } - } - } - } - - // debugging - def debugging[T](msg: String)(res: T) = { - repldbg(msg + " " + res) - res - } -} - -/** Utility methods for the Interpreter. */ -object IMain { - // The two name forms this is catching are the two sides of this assignment: - // - // $line3.$read.$iw.$iw.Bippy = - // $line3.$read$$iw$$iw$Bippy@4a6a00ca - private def removeLineWrapper(s: String) = s.replaceAll("""\$line\d+[./]\$(read|eval|print)[$.]""", "") - private def removeIWPackages(s: String) = s.replaceAll("""\$(iw|read|eval|print)[$.]""", "") - def stripString(s: String) = removeIWPackages(removeLineWrapper(s)) - - trait CodeAssembler[T] { - def preamble: String - def generate: T => String - def postamble: String - - def apply(contributors: List[T]): String = stringFromWriter { code => - code println preamble - contributors map generate foreach (code println _) - code println postamble - } - } - - trait StrippingWriter { - def isStripping: Boolean - def stripImpl(str: String): String - def strip(str: String): String = if (isStripping) stripImpl(str) else str - } - trait TruncatingWriter { - def maxStringLength: Int - def isTruncating: Boolean - def truncate(str: String): String = { - if (isTruncating && (maxStringLength != 0 && str.length > maxStringLength)) - (str take maxStringLength - 3) + "..." - else str - } - } - abstract class StrippingTruncatingWriter(out: JPrintWriter) - extends JPrintWriter(out) - with StrippingWriter - with TruncatingWriter { - self => - - def clean(str: String): String = truncate(strip(str)) - override def write(str: String) = super.write(clean(str)) - } - class ReplStrippingWriter(intp: IMain) extends StrippingTruncatingWriter(intp.out) { - import intp._ - def maxStringLength = isettings.maxPrintString - def isStripping = isettings.unwrapStrings - def isTruncating = reporter.truncationOK - - def stripImpl(str: String): String = naming.unmangle(str) - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala b/src/compiler/scala/tools/nsc/interpreter/ISettings.scala deleted file mode 100644 index 9541d08db1..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ISettings.scala +++ /dev/null @@ -1,54 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Alexander Spoon - */ - -package scala.tools.nsc -package interpreter - -/** Settings for the interpreter - * - * @version 1.0 - * @author Lex Spoon, 2007/3/24 - **/ -class ISettings(intp: IMain) { - /** The maximum length of toString to use when printing the result - * of an evaluation. 0 means no maximum. If a printout requires - * more than this number of characters, then the printout is - * truncated. - */ - var maxPrintString = replProps.maxPrintString.option.getOrElse(800) - - /** The maximum number of completion candidates to print for tab - * completion without requiring confirmation. - */ - var maxAutoprintCompletion = 250 - - /** String unwrapping can be disabled if it is causing issues. - * Setting this to false means you will see Strings like "$iw.$iw.". - */ - var unwrapStrings = true - - def deprecation_=(x: Boolean) = { - val old = intp.settings.deprecation.value - intp.settings.deprecation.value = x - if (!old && x) println("Enabled -deprecation output.") - else if (old && !x) println("Disabled -deprecation output.") - } - def deprecation: Boolean = intp.settings.deprecation.value - - def allSettings = Map[String, Any]( - "maxPrintString" -> maxPrintString, - "maxAutoprintCompletion" -> maxAutoprintCompletion, - "unwrapStrings" -> unwrapStrings, - "deprecation" -> deprecation - ) - - private def allSettingsString = - allSettings.toList sortBy (_._1) map { case (k, v) => " " + k + " = " + v + "\n" } mkString - - override def toString = """ - | ISettings { - | %s - | }""".stripMargin.format(allSettingsString) -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Imports.scala b/src/compiler/scala/tools/nsc/interpreter/Imports.scala deleted file mode 100644 index ff7bfd432c..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala +++ /dev/null @@ -1,181 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.collection.{ mutable, immutable } - -trait Imports { - self: IMain => - - import global._ - import definitions.{ ObjectClass, ScalaPackage, JavaLangPackage, PredefModule } - import memberHandlers._ - - /** Synthetic import handlers for the language defined imports. */ - private def makeWildcardImportHandler(sym: Symbol): ImportHandler = { - val hd :: tl = sym.fullName.split('.').toList map newTermName - val tree = Import( - tl.foldLeft(Ident(hd): Tree)((x, y) => Select(x, y)), - ImportSelector.wildList - ) - tree setSymbol sym - new ImportHandler(tree) - } - - /** Symbols whose contents are language-defined to be imported. */ - def languageWildcardSyms: List[Symbol] = List(JavaLangPackage, ScalaPackage, PredefModule) - def languageWildcardHandlers = languageWildcardSyms map makeWildcardImportHandler - - def allImportedNames = importHandlers flatMap (_.importedNames) - - /** Types which have been wildcard imported, such as: - * val x = "abc" ; import x._ // type java.lang.String - * import java.lang.String._ // object java.lang.String - * - * Used by tab completion. - * - * XXX right now this gets import x._ and import java.lang.String._, - * but doesn't figure out import String._. There's a lot of ad hoc - * scope twiddling which should be swept away in favor of digging - * into the compiler scopes. - */ - def sessionWildcards: List[Type] = { - importHandlers filter (_.importsWildcard) map (_.targetType) distinct - } - - def languageSymbols = languageWildcardSyms flatMap membersAtPickler - def sessionImportedSymbols = importHandlers flatMap (_.importedSymbols) - def importedSymbols = languageSymbols ++ sessionImportedSymbols - def importedTermSymbols = importedSymbols collect { case x: TermSymbol => x } - - /** Tuples of (source, imported symbols) in the order they were imported. - */ - def importedSymbolsBySource: List[(Symbol, List[Symbol])] = { - val lang = languageWildcardSyms map (sym => (sym, membersAtPickler(sym))) - val session = importHandlers filter (_.targetType != NoType) map { mh => - (mh.targetType.typeSymbol, mh.importedSymbols) - } - - lang ++ session - } - def implicitSymbolsBySource: List[(Symbol, List[Symbol])] = { - importedSymbolsBySource map { - case (k, vs) => (k, vs filter (_.isImplicit)) - } filterNot (_._2.isEmpty) - } - - /** Compute imports that allow definitions from previous - * requests to be visible in a new request. Returns - * three pieces of related code: - * - * 1. An initial code fragment that should go before - * the code of the new request. - * - * 2. A code fragment that should go after the code - * of the new request. - * - * 3. An access path which can be traversed to access - * any bindings inside code wrapped by #1 and #2 . - * - * The argument is a set of Names that need to be imported. - * - * Limitations: This method is not as precise as it could be. - * (1) It does not process wildcard imports to see what exactly - * they import. - * (2) If it imports any names from a request, it imports all - * of them, which is not really necessary. - * (3) It imports multiple same-named implicits, but only the - * last one imported is actually usable. - */ - case class ComputedImports(prepend: String, append: String, access: String) - protected def importsCode(wanted: Set[Name]): ComputedImports = { - /** Narrow down the list of requests from which imports - * should be taken. Removes requests which cannot contribute - * useful imports for the specified set of wanted names. - */ - case class ReqAndHandler(req: Request, handler: MemberHandler) { } - - def reqsToUse: List[ReqAndHandler] = { - /** Loop through a list of MemberHandlers and select which ones to keep. - * 'wanted' is the set of names that need to be imported. - */ - def select(reqs: List[ReqAndHandler], wanted: Set[Name]): List[ReqAndHandler] = { - // Single symbol imports might be implicits! See bug #1752. Rather than - // 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.definedNames exists wanted) - } - - reqs match { - case Nil => Nil - case rh :: rest if !keepHandler(rh.handler) => select(rest, wanted) - case rh :: rest => - import rh.handler._ - val newWanted = wanted ++ referencedNames -- definedNames -- importedNames - rh :: select(rest, newWanted) - } - } - - /** Flatten the handlers out and pair each with the original request */ - select(allReqAndHandlers reverseMap { case (r, h) => ReqAndHandler(r, h) }, wanted).reverse - } - - val code, trailingBraces, accessPath = new StringBuilder - val currentImps = mutable.HashSet[Name]() - - // add code for a new object to hold some imports - def addWrapper() { - val impname = nme.INTERPRETER_IMPORT_WRAPPER - code append "object %s {\n".format(impname) - trailingBraces append "}\n" - accessPath append ("." + impname) - currentImps.clear() - } - def maybeWrap(names: Name*) = if (names exists currentImps) addWrapper() - def wrapBeforeAndAfter[T](op: => T): T = { - addWrapper() - try op finally addWrapper() - } - - // loop through previous requests, adding imports for each one - wrapBeforeAndAfter { - for (ReqAndHandler(req, handler) <- reqsToUse) { - handler match { - // If the user entered an import, then just use it; add an import wrapping - // level if the import might conflict with some other import - case x: ImportHandler if x.importsWildcard => - wrapBeforeAndAfter(code append (x.member + "\n")) - case x: ImportHandler => - maybeWrap(x.importedNames: _*) - code append (x.member + "\n") - currentImps ++= x.importedNames - - // 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 (sym <- x.definedSymbols) { - maybeWrap(sym.name) - code append s"import ${x.path}\n" - currentImps += sym.name - } - } - } - } - - ComputedImports(code.toString, trailingBraces.toString, accessPath.toString) - } - - private def allReqAndHandlers = - prevRequestList flatMap (req => req.handlers map (req -> _)) - - private def membersAtPickler(sym: Symbol): List[Symbol] = - enteringPickler(sym.info.nonPrivateMembers.toList) -} diff --git a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala b/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala deleted file mode 100644 index 28ddf2939c..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/InteractiveReader.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Stepan Koltsov - */ - -package scala.tools.nsc -package interpreter - -import java.io.IOException -import session.History -import InteractiveReader._ -import Properties.isMac - -/** Reads lines from an input stream */ -trait InteractiveReader { - val interactive: Boolean - - def reset(): Unit - def history: History - def completion: Completion - def redrawLine(): Unit - - def readYesOrNo(prompt: String, alt: => Boolean): Boolean = readOneKey(prompt) match { - case 'y' => true - case 'n' => false - case _ => alt - } - - protected def readOneLine(prompt: String): String - protected def readOneKey(prompt: String): Int - - def readLine(prompt: String): String = - // hack necessary for OSX jvm suspension because read calls are not restarted after SIGTSTP - if (isMac) restartSysCalls(readOneLine(prompt), reset()) - else readOneLine(prompt) -} - -object InteractiveReader { - val msgEINTR = "Interrupted system call" - def restartSysCalls[R](body: => R, reset: => Unit): R = - try body catch { - case e: IOException if e.getMessage == msgEINTR => reset ; body - } - - def apply(): InteractiveReader = SimpleReader() - @deprecated("Use `apply` instead.", "2.9.0") - def createDefault(): InteractiveReader = apply() // used by sbt -} - diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala deleted file mode 100644 index 19fa562234..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala +++ /dev/null @@ -1,352 +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 - -// REPL completor - queries supplied interpreter for valid -// completions based on current contents of buffer. -class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput { - val global: intp.global.type = intp.global - import global._ - import definitions.{ PredefModule, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage } - import rootMirror.{ RootClass, getModuleIfDefined } - type ExecResult = Any - 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 = AnyClass.tpe.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 (_.isPackage) - 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(AnyRefClass.tpe) { } - - // 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 getOrElse null) - 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): Tree = newUnitParser(code).templateStats().last - def completions(verbosity: Int) = Nil - - override def follow(id: String) = simpleParse(id) match { - 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("\ncomplete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, 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("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format( - p, lastBuf, lastCursor, 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/compiler/scala/tools/nsc/interpreter/JLineReader.scala b/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala deleted file mode 100644 index 5d41f1bbb4..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/JLineReader.scala +++ /dev/null @@ -1,68 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Stepan Koltsov - */ - -package scala.tools.nsc -package interpreter - -import scala.tools.jline.console.ConsoleReader -import scala.tools.jline.console.completer._ -import session._ -import Completion._ - -/** - * Reads from the console using JLine. - */ -class JLineReader(_completion: => Completion) extends InteractiveReader { - val interactive = true - val consoleReader = new JLineConsoleReader() - - lazy val completion = _completion - lazy val history: JLineHistory = JLineHistory() - - private def term = consoleReader.getTerminal() - def reset() = term.reset() - - def scalaToJline(tc: ScalaCompleter): Completer = new Completer { - def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = { - val buf = if (_buf == null) "" else _buf - val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor) - newCandidates foreach (candidates add _) - newCursor - } - } - - class JLineConsoleReader extends ConsoleReader with ConsoleReaderHelper { - // working around protected/trait/java insufficiencies. - def goBack(num: Int): Unit = back(num) - if ((history: History) ne NoHistory) - this setHistory history - - def readOneKey(prompt: String) = { - this.print(prompt) - this.flush() - this.readVirtualKey() - } - def eraseLine() = consoleReader.resetPromptLine("", "", 0) - def redrawLineAndFlush(): Unit = { flush() ; drawLine() ; flush() } - - // A hook for running code after the repl is done initializing. - lazy val postInit: Unit = { - this setBellEnabled false - - if (completion ne NoCompletion) { - val argCompletor: ArgumentCompleter = - new ArgumentCompleter(new JLineDelimiter, scalaToJline(completion.completer())) - argCompletor setStrict false - - this addCompleter argCompletor - this setAutoprintThreshold 400 // max completion candidates without warning - } - } - } - - def redrawLine() = consoleReader.redrawLineAndFlush() - def readOneLine(prompt: String) = consoleReader readLine prompt - def readOneKey(prompt: String) = consoleReader readOneKey prompt -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Logger.scala b/src/compiler/scala/tools/nsc/interpreter/Logger.scala deleted file mode 100644 index 7407daf8d0..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Logger.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -trait Logger { - def isInfo: Boolean - def isDebug: Boolean - def isTrace: Boolean - def out: JPrintWriter -} diff --git a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala b/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala deleted file mode 100644 index 4bba27b714..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/LoopCommands.scala +++ /dev/null @@ -1,86 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.collection.{ mutable, immutable } -import mutable.ListBuffer -import scala.language.implicitConversions - -class ProcessResult(val line: String) { - import scala.sys.process._ - private val buffer = new ListBuffer[String] - - val builder = Process(line) - val logger = ProcessLogger(buffer += _) - val exitCode = builder ! logger - def lines = buffer.toList - - override def toString = "`%s` (%d lines, exit %d)".format(line, buffer.size, exitCode) -} - -trait LoopCommands { - protected def out: JPrintWriter - - // So outputs can be suppressed. - def echoCommandMessage(msg: String): Unit = out println msg - - // a single interpreter command - abstract class LoopCommand(val name: String, val help: String) extends (String => Result) { - def usage: String = "" - def usageMsg: String = ":" + name + ( - if (usage == "") "" else " " + usage - ) - def apply(line: String): Result - - // called if no args are given - def showUsage(): Result = { - "usage is " + usageMsg - Result(keepRunning = true, None) - } - } - object LoopCommand { - def nullary(name: String, help: String, f: () => Result): LoopCommand = - new NullaryCmd(name, help, _ => f()) - - def cmd(name: String, usage: String, help: String, f: String => Result): LoopCommand = - if (usage == "") new NullaryCmd(name, help, f) - else new LineCmd(name, usage, help, f) - } - - class NullaryCmd(name: String, help: String, f: String => Result) extends LoopCommand(name, help) { - def apply(line: String): Result = f(line) - } - - class LineCmd(name: String, argWord: String, help: String, f: String => Result) extends LoopCommand(name, help) { - override def usage = argWord - def apply(line: String): Result = f(line) - } - - class VarArgsCmd(name: String, argWord: String, help: String, f: List[String] => Result) - extends LoopCommand(name, help) { - override def usage = argWord - def apply(line: String): Result = apply(words(line)) - def apply(args: List[String]) = f(args) - } - - // the result of a single command - case class Result(keepRunning: Boolean, lineToRecord: Option[String]) - - object Result { - // the default result means "keep running, and don't record that line" - val default = Result(keepRunning = true, None) - - // most commands do not want to micromanage the Result, but they might want - // to print something to the console, so we accomodate Unit and String returns. - implicit def resultFromUnit(x: Unit): Result = default - implicit def resultFromString(msg: String): Result = { - echoCommandMessage(msg) - default - } - } -} - diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala deleted file mode 100644 index 84a47311e2..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ /dev/null @@ -1,219 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package interpreter - -import scala.collection.{ mutable, immutable } -import scala.reflect.internal.Flags._ -import scala.language.implicitConversions - -trait MemberHandlers { - val intp: IMain - - import intp.{ Request, global, naming } - import global._ - import naming._ - - 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(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 - } - } - - private def isTermMacro(ddef: DefDef): Boolean = ddef.mods.isMacro - - def chooseHandler(member: Tree): MemberHandler = member match { - case member: DefDef if isTermMacro(member) => new TermMacroHandler(member) - case member: DefDef => new DefHandler(member) - case member: ValDef => new ValHandler(member) - case member: ModuleDef => new ModuleHandler(member) - case member: ClassDef => new ClassHandler(member) - case member: TypeDef => new TypeAliasHandler(member) - case member: Assign => new AssignHandler(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) { - override def name: Name = member.name - def mods: Modifiers = member.mods - def keyword = member.keyword - def prettyName = name.decode - - 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) - override def definedSymbols = if (symbol eq NoSymbol) Nil else List(symbol) - } - - /** Class to handle one member among all the members included - * in a single interpreter request. - */ - sealed abstract class MemberHandler(val member: Tree) { - def name: Name = nme.NO_NAME - def path = intp.originalPath(symbol) - def symbol = if (member.symbol eq null) NoSymbol else member.symbol - 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 definedSymbols = List[Symbol]() - - 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 - 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, "<lazy>") - else any2stringOf(path, maxStringElements) - - val vidString = - if (replProps.vids) s"""" + " @ " + "%%8x".format(System.identityHashCode($path)) + " """.trim - else "" - - """ + "%s%s: %s = " + %s""".format(string2code(prettyName), vidString, string2code(req typeOf name), resultString) - } - } - } - - class DefHandler(member: DefDef) extends MemberDefHandler(member) { - override def definesValue = flattensToEmpty(member.vparamss) // true if 0-arity - override def resultExtractionCode(req: Request) = - if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else "" - } - - abstract class MacroHandler(member: DefDef) extends MemberDefHandler(member) { - override def definesValue = false - override def definesTerm: Option[TermName] = Some(name.toTermName) - override def definesType: Option[TypeName] = None - override def resultExtractionCode(req: Request) = if (mods.isPublic) codegenln(notification(req)) else "" - def notification(req: Request): String - } - - class TermMacroHandler(member: DefDef) extends MacroHandler(member) { - def notification(req: Request) = s"defined term macro $name: ${req.typeOf(name)}" - } - - class AssignHandler(member: Assign) extends MemberHandler(member) { - val Assign(lhs, rhs) = member - override lazy 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(string2code(lhs.toString), lhsType, res) + "\n" - } - } - - class ModuleHandler(module: ModuleDef) extends MemberDefHandler(module) { - override def definesTerm = Some(name.toTermName) - override def definesValue = true - - override def resultExtractionCode(req: Request) = codegenln("defined object ", name) - } - - class ClassHandler(member: ClassDef) extends MemberDefHandler(member) { - override def definedSymbols = List(symbol, symbol.companionSymbol) filterNot (_ == NoSymbol) - 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 = intp.global.rootMirror.getModuleIfDefined("" + expr) match { - case NoSymbol => intp.typeOfExpression("" + expr) - case sym => sym.thisType - } - private def importableTargetMembers = importableMembers(targetType).toList - // wildcard imports, e.g. import foo._ - private def selectorWild = selectors filter (_.name == nme.USCOREkw) - // renamed imports, e.g. import foo.{ bar => baz } - private def selectorRenames = selectors map (_.rename) filterNot (_ == null) - - /** Whether this import includes a wildcard import */ - val importsWildcard = selectorWild.nonEmpty - - def implicitSymbols = importedSymbols filter (_.isImplicit) - def importedSymbols = individualSymbols ++ wildcardSymbols - - private val selectorNames = selectorRenames filterNot (_ == nme.USCOREkw) flatMap (_.bothNames) toSet - lazy val individualSymbols: List[Symbol] = exitingTyper(importableTargetMembers filter (m => selectorNames(m.name))) - lazy val wildcardSymbols: List[Symbol] = exitingTyper(if (importsWildcard) importableTargetMembers else Nil) - - /** Complete list of names imported by a wildcard */ - lazy val wildcardNames: List[Name] = wildcardSymbols map (_.name) - lazy val individualNames: List[Name] = individualSymbols map (_.name) - - /** The names imported by this statement */ - override lazy val importedNames: List[Name] = wildcardNames ++ individualNames - lazy val importsSymbolNamed: Set[String] = importedNames map (_.toString) toSet - - def importString = imp.toString - override def resultExtractionCode(req: Request) = codegenln(importString) + "\n" - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala b/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala deleted file mode 100644 index 627a881cae..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/NamedParam.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import NamedParam._ -import scala.language.implicitConversions -import scala.reflect.runtime.{universe => ru} -import scala.reflect.{ClassTag, classTag} - -trait NamedParamCreator { - protected def freshName: () => String - - def apply[T: ru.TypeTag : ClassTag](name: String, x: T): NamedParam = new Typed[T](name, x) - def apply[T: ru.TypeTag : ClassTag](x: T): NamedParam = apply(freshName(), x) - def clazz(name: String, x: Any): NamedParam = new Untyped(name, x) - - implicit def tuple[T: ru.TypeTag : ClassTag](pair: (String, T)): NamedParam = apply(pair._1, pair._2) -} - -object NamedParam extends NamedParamCreator { - class Typed[T: ru.TypeTag : ClassTag](val name: String, val value: T) extends NamedParam { - val tpe = TypeStrings.fromTag[T] - } - class Untyped(val name: String, val value: Any) extends NamedParam { - val tpe = TypeStrings.fromValue(value) - } - - protected val freshName = { - var counter = 0 - () => { counter += 1; "p" + counter } - } -} - -case class NamedParamClass(name: String, tpe: String, value: Any) extends NamedParam { } - -trait NamedParam { - def name: String - def tpe: String - def value: Any - override def toString = name + ": " + tpe -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Naming.scala b/src/compiler/scala/tools/nsc/interpreter/Naming.scala deleted file mode 100644 index 57f3675ada..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Naming.scala +++ /dev/null @@ -1,105 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.util.Properties.lineSeparator - -/** This is for name logic which is independent of the compiler (notice there's no Global.) - * That includes at least generating, metaquoting, mangling, and unmangling. - */ -trait Naming { - def unmangle(str: String): String = { - val ESC = '\u001b' - val cleaned = removeIWPackages(removeLineWrapper(str)) - // Looking to exclude binary data which hoses the terminal, but - // let through the subset of it we need, like whitespace and also - // <ESC> for ansi codes. - val binaryChars = cleaned count (ch => ch < 32 && !ch.isWhitespace && ch != ESC) - // Lots of binary chars - translate all supposed whitespace into spaces - // except supposed line endings, otherwise scrubbed lines run together - if (binaryChars > 5) // more than one can count while holding a hamburger - cleaned map { - case c if lineSeparator contains c => c - case c if c.isWhitespace => ' ' - case c if c < 32 => '?' - case c => c - } - // Not lots - preserve whitespace and ESC - else - cleaned map (ch => if (ch.isWhitespace || ch == ESC) ch else if (ch < 32) '?' else ch) - } - - // The two name forms this is catching are the two sides of this assignment: - // - // $line3.$read.$iw.$iw.Bippy = - // $line3.$read$$iw$$iw$Bippy@4a6a00ca - - private def noMeta(s: String) = "\\Q" + s + "\\E" - private lazy val lineRegex = { - val sn = sessionNames - val members = List(sn.read, sn.eval, sn.print) map noMeta mkString ("(?:", "|", ")") - debugging("lineRegex")(noMeta(sn.line) + """\d+[./]""" + members + """[$.]""") - } - - private def removeLineWrapper(s: String) = s.replaceAll(lineRegex, "") - private def removeIWPackages(s: String) = s.replaceAll("""\$iw[$.]""", "") - - trait SessionNames { - // All values are configurable by passing e.g. -Dscala.repl.name.read=XXX - final def propOr(name: String): String = propOr(name, "$" + name) - final def propOr(name: String, default: String): String = - sys.props.getOrElse("scala.repl.name." + name, default) - - // Prefixes used in repl machinery. Default to $line, $read, etc. - def line = propOr("line") - def read = propOr("read") - def eval = propOr("eval") - def print = propOr("print") - def result = propOr("result") - - // The prefix for unnamed results: by default res0, res1, etc. - def res = propOr("res", "res") // INTERPRETER_VAR_PREFIX - // Internal ones - def ires = propOr("ires") - } - lazy val sessionNames: SessionNames = new SessionNames { } - - /** 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 - mostRecent - } - def reset(): Unit = x = -1 - def didGenerate(name: String) = - (name startsWith pre) && ((name drop pre.length) forall (_.isDigit)) - } - - private lazy val userVar = new NameCreator(sessionNames.res) // var name, like res0 - private lazy val internalVar = new NameCreator(sessionNames.ires) // internal var name, like $ires0 - - def isUserVarName(name: String) = userVar didGenerate name - def isInternalVarName(name: String) = internalVar didGenerate name - - val freshLineId = { - var x = 0 - () => { x += 1 ; x } - } - def freshUserVarName() = userVar() - def freshInternalVarName() = internalVar() - - def resetAllCreators() { - userVar.reset() - internalVar.reset() - } - - def mostRecentVar = userVar.mostRecent -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala deleted file mode 100644 index 672a6fd28f..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala +++ /dev/null @@ -1,60 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import util.returning - -/** One instance of a command buffer. - */ -class Parsed private ( - val buffer: String, - val cursor: Int, - val delimited: Char => Boolean -) extends Delimited { - def isEmpty = args.isEmpty - def isUnqualified = args.size == 1 - def isAtStart = cursor <= 0 - - private var _verbosity = 0 - - def verbosity = _verbosity - def withVerbosity(v: Int): this.type = returning[this.type](this)(_ => _verbosity = v) - - def args = toArgs(buffer take cursor).toList - def bufferHead = args.head - def headLength = bufferHead.length + 1 - def bufferTail = new Parsed(buffer drop headLength, cursor - headLength, delimited) withVerbosity verbosity - - def prev = new Parsed(buffer, cursor - 1, delimited) withVerbosity verbosity - def currentChar = buffer(cursor) - def currentArg = args.last - def position = - if (isEmpty) 0 - else if (isLastDelimiter) cursor - else cursor - currentArg.length - - def isFirstDelimiter = !isEmpty && isDelimiterChar(buffer.head) - def isLastDelimiter = !isEmpty && isDelimiterChar(buffer.last) - - def isQuoted = false // TODO - def isEscaped = !isAtStart && isEscapeChar(currentChar) && !isEscapeChar(prev.currentChar) - def isDelimiter = !isQuoted && !isEscaped && isDelimiterChar(currentChar) - - override def toString = "Parsed(%s / %d)".format(buffer, cursor) -} - -object Parsed { - val DefaultDelimiters = "[]{},`; \t".toSet - - private def onull(s: String) = if (s == null) "" else s - - def apply(s: String, cursor: Int): Parsed = apply(onull(s), cursor, DefaultDelimiters) - def apply(s: String, cursor: Int, delimited: Char => Boolean): Parsed = - new Parsed(onull(s), cursor, delimited) - - def dotted(s: String, cursor: Int): Parsed = new Parsed(onull(s), cursor, _ == '.') -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Pasted.scala b/src/compiler/scala/tools/nsc/interpreter/Pasted.scala deleted file mode 100644 index f5db3d9e3a..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Pasted.scala +++ /dev/null @@ -1,101 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -/** If it looks like they're pasting in a scala interpreter - * transcript, remove all the formatting we inserted so we - * can make some sense of it. - * - * Most of the interesting code in here is due to my goal of - * "paste idempotence" i.e. the transcript resulting from pasting - * a transcript should itself be pasteable and should achieve - * the same result. - */ -abstract class Pasted { - def ContinueString: String - def PromptString: String - def interpret(line: String): Unit - - def matchesPrompt(line: String) = matchesString(line, PromptString) - def matchesContinue(line: String) = matchesString(line, ContinueString) - def running = isRunning - - private def matchesString(line: String, target: String): Boolean = ( - (line startsWith target) || - (line.nonEmpty && " \t".toSet(line.head) && matchesString(line.tail, target)) - ) - private def stripString(line: String, target: String) = line indexOf target match { - case -1 => line - case idx => line drop (idx + target.length) - } - private var isRunning = false - private val resReference = """(?<!^)(res\d+)""".r - private val resCreation = """^\s*(res\d+):.*""".r - private val resAssign = """^val (res\d+).*""".r - - private class PasteAnalyzer(val lines: List[String]) { - val referenced = lines flatMap (resReference findAllIn _.trim.stripPrefix("res")) toSet - val cmds = lines reduceLeft append split PromptString filterNot (_.trim == "") toList - - /** If it's a prompt or continuation line, strip the formatting bits and - * assemble the code. Otherwise ship it off to be analyzed for res references - * and discarded. - */ - def append(code: String, line: String): String = - if (matchesPrompt(line)) code + "\n" + line - else if (matchesContinue(line)) code + "\n" + stripString(line, ContinueString) - else fixResRefs(code, line) - - /** If the line looks like - * res15: Int - * - * and the additional conditions hold that: - * 1) res15 is referenced from elsewhere in the transcript - * 2) the preceding repl line is not "val res15 = ..." because that - * indicates it has already been "val-ified" on a previous paste - * - * then we go back in time to the preceding scala> prompt and - * rewrite the line containing <expr> as - * val res15 = { <expr> } - * and the rest as they say is rewritten history. - * - * In all other cases, discard the line. - */ - def fixResRefs(code: String, line: String) = line match { - case resCreation(resName) if referenced(resName) => - code.lastIndexOf(PromptString) match { - case -1 => code - case idx => - val (str1, str2) = code splitAt (idx + PromptString.length) - str2 match { - case resAssign(`resName`) => code - case _ => "%sval %s = { %s }".format(str1, resName, str2) - } - } - case _ => code - } - - def run() { - println("// Replaying %d commands from transcript.\n" format cmds.size) - cmds foreach { cmd => - print(PromptString) - interpret(cmd) - } - } - } - - /** Commands start on lines beginning with "scala>" and each successive - * line which begins with the continuation string is appended to that command. - * Everything else is discarded. When the end of the transcript is spotted, - * all the commands are replayed. - */ - def apply(lines: TraversableOnce[String]) = { - isRunning = true - try new PasteAnalyzer(lines.toList) run() - finally isRunning = false - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Phased.scala b/src/compiler/scala/tools/nsc/interpreter/Phased.scala deleted file mode 100644 index f625124e70..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Phased.scala +++ /dev/null @@ -1,143 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -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 - * swiss army knife. - */ -trait Phased { - val global: Global - import global._ - - private var active: PhaseName = NoPhaseName - private var multi: Seq[PhaseName] = Nil - - def get = active - def set(phase: PhaseName): Boolean = phase match { - case NoPhaseName => false - case name => active = name ; true - } - def setMulti(phases: Seq[PhaseName]): Boolean = { - if (phases contains NoPhaseName) false - else { - multi = phases - true - } - } - - private def parsePhaseChange(str: String): Option[Int] = { - if (str == "") Some(0) - else if (str startsWith ".prev") parsePhaseChange(str drop 5) map (_ - 1) - else if (str startsWith ".next") parsePhaseChange(str drop 5) map (_ + 1) - else str.head match { - case '+' | '-' => - val (num, rest) = str.tail.span(_.isDigit) - val diff = if (str.head == '+') num.toInt else -num.toInt - parsePhaseChange(rest) map (_ + diff) - case _ => - None - } - } - - /** Takes a string like 4, typer+2, typer.next, etc. - * and turns it into a PhaseName instance. - */ - private def parseInternal(str: String): PhaseName = { - if (str == "") NoPhaseName - else if (str forall (_.isDigit)) PhaseName(str.toInt) - else { - val (name, rest) = str.toLowerCase span (_.isLetter) - val start = PhaseName(name) - val change = parsePhaseChange(rest) - - if (start.isEmpty || change.isEmpty) NoPhaseName - else PhaseName(start.id + change.get) - } - } - def parse(str: String): PhaseName = - try parseInternal(str) - catch { case _: Exception => NoPhaseName } - - def atCurrent[T](body: => T): T = enteringPhase(get)(body) - def multi[T](body: => T): Seq[T] = multi map (ph => at(ph)(body)) - - def at[T](ph: PhaseName)(body: => T): T = { - val saved = get - set(ph) - try atCurrent(body) - finally set(saved) - } - def atMulti[T](phs: Seq[PhaseName])(body: => T): Seq[T] = { - val saved = multi - setMulti(phs) - try multi(body) - finally setMulti(saved) - } - - def atMap[T](phs: Seq[PhaseName])(body: => T): Seq[(PhaseName, T)] = - phs zip atMulti(phs)(body) - - object PhaseName { - implicit lazy val phaseNameOrdering: Ordering[PhaseName] = Ordering[Int] on (_.id) - - 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, Icode, Inliner, Closelim, Dce, Jvm, Terminal - ) - lazy val nameMap = all.map(x => x.name -> x).toMap withDefaultValue NoPhaseName - multi = all - - def apply(id: Int): PhaseName = all find (_.id == id) getOrElse NoPhaseName - implicit def apply(s: String): PhaseName = nameMap(s) - } - sealed abstract class PhaseName { - lazy val id = phase.id - lazy val name = toString.toLowerCase - def phase = currentRun.phaseNamed(name) - def isEmpty = this eq NoPhaseName - } - - case object Parser extends PhaseName - case object Namer extends PhaseName - case object Packageobjects extends PhaseName - case object Typer extends PhaseName - case object Superaccessors 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 Lazyvals extends PhaseName - case object Lambdalift extends PhaseName - case object Constructors extends PhaseName - case object Flatten extends PhaseName - case object Mixin extends PhaseName - case object Cleanup 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 { - override lazy val id = -1 - override lazy val name = phase.name - override def phase = NoPhase - } - - implicit def phaseEnumToPhase(name: PhaseName): Phase = name.phase -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala deleted file mode 100644 index e517a16b32..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ /dev/null @@ -1,326 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -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.reflect.runtime.{universe => ru} -import scala.reflect.{ClassTag, classTag} - -/** Collecting some power mode examples. - -scala> trait F[@specialized(Int) T] { def f: T = ??? } -defined trait F - -scala> trait G[@specialized(Long, Int) T] extends F[T] { override def f: T = super.f } -defined trait G - -scala> changesAfterEachPhase(intp("G").info.members filter (_.name.toString contains "super")) > -Gained after 1/parser { - method super$f -} - -Gained after 12/specialize { - method super$f$mcJ$sp - method super$f$mcI$sp -} - -Lost after 18/flatten { - method super$f$mcJ$sp - method super$f$mcI$sp - method super$f -} -*/ - -/** 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.global._ - import definitions.{ compilerTypeFromTag, compilerSymbolFromTag} - - abstract class SymSlurper { - def isKeep(sym: Symbol): Boolean - def isIgnore(sym: Symbol): Boolean - def isRecur(sym: Symbol): Boolean - def isFinished(): Boolean - - val keep = mutable.HashSet[Symbol]() - val seen = mutable.HashSet[Symbol]() - def processed = keep.size + seen.size - def discarded = seen.size - keep.size - - def members(x: Symbol): List[Symbol] = - if (x.rawInfo.isComplete) x.info.members.toList - else Nil - - var lastCount = -1 - var pass = 0 - val unseenHistory = new mutable.ListBuffer[Int] - - def loop(todo: Set[Symbol]): Set[Symbol] = { - pass += 1 - val (repeats, unseen) = todo partition seen - unseenHistory += unseen.size - if (settings.verbose.value) { - println("%3d %s accumulated, %s discarded. This pass: %s unseen, %s repeats".format( - pass, keep.size, discarded, unseen.size, repeats.size)) - } - if (lastCount == processed || unseen.isEmpty || isFinished()) - return keep.toSet - - lastCount = processed - keep ++= (unseen filter isKeep filterNot isIgnore) - seen ++= unseen - loop(unseen filter isRecur flatMap members) - } - - def apply(sym: Symbol): Set[Symbol] = { - keep.clear() - seen.clear() - loop(Set(sym)) - } - } - - class PackageSlurper(packageClass: Symbol) extends SymSlurper { - /** Looking for dwindling returns */ - def droppedEnough() = unseenHistory.size >= 4 && { - unseenHistory takeRight 4 sliding 2 forall { it => - val List(a, b) = it.toList - a > b - } - } - - def isRecur(sym: Symbol) = true - def isIgnore(sym: Symbol) = sym.isAnonOrRefinementClass || (sym.name.toString contains "$mc") - def isKeep(sym: Symbol) = sym.hasTransOwner(packageClass) - def isFinished() = droppedEnough() - def slurp() = { - if (packageClass.isPackageClass) - apply(packageClass) - else { - repldbg("Not a package class! " + packageClass) - Set() - } - } - } - - private def customBanner = replProps.powerBanner.option flatMap (f => io.File(f).safeSlurp()) - private def customInit = replProps.powerInitCode.option flatMap (f => io.File(f).safeSlurp()) - - def banner = customBanner getOrElse """ - |** Power User mode enabled - BEEP WHIR GYVE ** - |** :phase has been set to 'typer'. ** - |** scala.tools.nsc._ has been imported ** - |** global._, definitions._ also imported ** - |** Try :help, :vals, power.<tab> ** - """.stripMargin.trim - - private def initImports = List( - "scala.tools.nsc._", - "scala.collection.JavaConverters._", - "intp.global.{ error => _, _ }", - "definitions.{ getClass => _, _ }", - "power.rutil._", - "replImplicits._", - "treedsl.CODE._" - ) - - def init = customInit match { - case Some(x) => x - case _ => initImports.mkString("import ", ", ", "") - } - - /** Starts up power mode and runs whatever is in init. - */ - def unleash(): Unit = beQuietDuring { - // First we create the ReplVals instance and bind it to $r - intp.bind("$r", replVals) - // Then we import everything from $r. - intp interpret ("import " + intp.originalPath("$r") + "._") - // And whatever else there is to do. - init.lines foreach (intp interpret _) - } - - trait LowPriorityInternalInfo { - implicit def apply[T: ru.TypeTag : ClassTag] : InternalInfo[T] = new InternalInfo[T](None) - } - object InternalInfo extends LowPriorityInternalInfo { } - - /** Now dealing with the problem of acidentally calling a method on Type - * when you're holding a Symbol and seeing the Symbol converted to the - * type of Symbol rather than the type of the thing represented by the - * symbol, by only implicitly installing one method, "?", and the rest - * of the conveniences exist on that wrapper. - */ - trait LowPriorityInternalInfoWrapper { } - class InternalInfoWrapper[T: ru.TypeTag : ClassTag](value: Option[T] = None) { - def ? : InternalInfo[T] = new InternalInfo[T](value) - } - - /** Todos... - * translate tag type arguments into applied types - * customizable symbol filter (had to hardcode no-spec to reduce noise) - */ - 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 - ) - def symbol = compilerSymbolFromTag(tag) - def tpe = compilerTypeFromTag(tag) - def members = membersUnabridged filterNot excludeMember - def membersUnabridged = tpe.members.toList - def pkg = symbol.enclosingPackage - def tag = typeEvidence - def runtimeClass = runtimeClassEvidence.runtimeClass - def shortClass = runtimeClass.getName split "[$.]" last - def baseClasses = tpe.baseClasses - - override def toString = value match { - case Some(x) => "%s (%s)".format(x, shortClass) - case _ => runtimeClass.getName - } - } - - trait LowPriorityPrettifier { - implicit object AnyPrettifier extends Prettifier[Any] { - def show(x: Any): Unit = prettify(x) foreach println - def prettify(x: Any): TraversableOnce[String] = x match { - case x: Name => List(x.decode) - case Tuple2(k, v) => List(prettify(k).toIterator ++ Iterator("->") ++ prettify(v) mkString " ") - case xs: Array[_] => xs.iterator flatMap prettify - case xs: TraversableOnce[_] => xs flatMap prettify - case x => List(Prettifier.stringOf(x)) - } - } - } - object StringPrettifier extends Prettifier[String] { - def show(x: String) = println(x) - def prettify(x: String) = List(Prettifier stringOf x) - } - object Prettifier extends LowPriorityPrettifier { - def stringOf(x: Any): String = scala.runtime.ScalaRunTime.stringOf(x) - def default[T] = new Prettifier[T] { - def prettify(x: T): TraversableOnce[String] = AnyPrettifier prettify x - def show(x: T): Unit = AnyPrettifier show x - } - } - trait Prettifier[T] { - def show(x: T): Unit - def prettify(x: T): TraversableOnce[String] - - def prettify(xs: TraversableOnce[T]): TraversableOnce[String] = xs flatMap (x => prettify(x)) - } - - abstract class PrettifierClass[T: Prettifier]() { - val pretty = implicitly[Prettifier[T]] - def value: Seq[T] - - def pp(f: Seq[T] => Seq[T]): Unit = - pretty prettify f(value) foreach (StringPrettifier show _) - - def freq[U](p: T => U) = (value.toSeq groupBy p mapValues (_.size)).toList sortBy (-_._2) map (_.swap) - - def >>(implicit ord: Ordering[T]): Unit = pp(_.sorted) - def >!(): Unit = pp(_.distinct) - def >(): Unit = pp(identity) - } - - class MultiPrettifierClass[T: Prettifier](val value: Seq[T]) extends PrettifierClass[T]() { } - class SinglePrettifierClass[T: Prettifier](single: T) extends PrettifierClass[T]() { - val value = List(single) - } - - class RichReplString(s: String) { - // make an url out of the string - def u: URL = ( - if (s contains ":") new URL(s) - else if (new JFile(s) exists) new JFile(s).toURI.toURL - else new URL("http://" + s) - ) - } - class RichInputStream(in: InputStream)(implicit codec: Codec) { - def bytes(): Array[Byte] = io.Streamable.bytes(in) - def slurp(): String = io.Streamable.slurp(in) - def <<(): String = slurp() - } - class RichReplURL(url: URL)(implicit codec: Codec) { - def slurp(): String = io.Streamable.slurp(url) - } - - trait Implicits1 { - // fallback - implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = - new SinglePrettifierClass[T](x) - } - trait Implicits2 extends Implicits1 { - class RichSymbol(sym: Symbol) { - // convenient type application - def apply(targs: Type*): Type = typeRef(NoPrefix, sym, targs.toList) - } - object symbolSubtypeOrdering extends Ordering[Symbol] { - def compare(s1: Symbol, s2: Symbol) = - if (s1 eq s2) 0 - else if (s1 isLess s2) -1 - else 1 - } - implicit lazy val powerSymbolOrdering: Ordering[Symbol] = Ordering[Name] on (_.name) - implicit lazy val powerTypeOrdering: Ordering[Type] = Ordering[Symbol] on (_.typeSymbol) - - implicit def replInternalInfo[T: ru.TypeTag : ClassTag](x: T): InternalInfoWrapper[T] = new InternalInfoWrapper[T](Some(x)) - implicit def replEnhancedStrings(s: String): RichReplString = new RichReplString(s) - implicit def replMultiPrinting[T: Prettifier](xs: TraversableOnce[T]): MultiPrettifierClass[T] = - new MultiPrettifierClass[T](xs.toSeq) - implicit def replPrettifier[T] : Prettifier[T] = Prettifier.default[T] - implicit def replTypeApplication(sym: Symbol): RichSymbol = new RichSymbol(sym) - - implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in) - implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec) - } - - trait ReplUtilities { - def module[T: ru.TypeTag] = ru.typeOf[T].typeSymbol.suchThat(_.isPackage) - def clazz[T: ru.TypeTag] = ru.typeOf[T].typeSymbol.suchThat(_.isClass) - def info[T: ru.TypeTag : ClassTag] = InternalInfo[T] - def ?[T: ru.TypeTag : ClassTag] = InternalInfo[T] - def sanitize(s: String): String = sanitize(s.getBytes()) - def sanitize(s: Array[Byte]): String = (s map { - case x if x.toChar.isControl => '?' - case x => x.toChar - }).mkString - - def strings(s: Seq[Byte]): List[String] = { - if (s.length == 0) Nil - else s dropWhile (_.toChar.isControl) span (x => !x.toChar.isControl) match { - case (next, rest) => next.map(_.toChar).mkString :: strings(rest) - } - } - } - - lazy val rutil: ReplUtilities = new ReplUtilities { } - lazy val phased: Phased = new { val global: intp.global.type = intp.global } with Phased { } - - def unit(code: String) = newCompilationUnit(code) - def trees(code: String) = parse(code) getOrElse Nil - - override def toString = s""" - |** Power mode status ** - |Default phase: ${phased.get} - |Names: ${intp.unqualifiedIds mkString " "} - """.stripMargin -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala b/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala deleted file mode 100644 index 3392ea0b5e..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ReplConfig.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.util.control.ControlThrowable -import util.Exceptional.unwrap -import util.stackTraceString - -trait ReplConfig { - lazy val replProps = new ReplProps - - class TapMaker[T](x: T) { - def tapDebug(msg: => String): T = tap(x => repldbg(parens(x))) - def tap[U](f: T => U): T = { - f(x) - x - } - } - - private def parens(x: Any) = "(" + x + ")" - private def echo(msg: => String) = - try Console println msg - catch { case x: AssertionError => Console.println("Assertion error printing debugging output: " + x) } - - private[nsc] def repldbg(msg: => String) = if (isReplDebug) echo(msg) - private[nsc] def repltrace(msg: => String) = if (isReplTrace) echo(msg) - private[nsc] def replinfo(msg: => String) = if (isReplInfo) echo(msg) - - private[nsc] def logAndDiscard[T](label: String, alt: => T): PartialFunction[Throwable, T] = { - case t: ControlThrowable => throw t - case t: Throwable => - repldbg(label + ": " + unwrap(t)) - repltrace(stackTraceString(unwrap(t))) - alt - } - private[nsc] def substituteAndLog[T](label: String, alt: => T)(body: => T): T = { - try body - catch logAndDiscard(label, alt) - } - - def isReplTrace: Boolean = replProps.trace - def isReplDebug: Boolean = replProps.debug || isReplTrace - def isReplInfo: Boolean = replProps.info || isReplDebug - def isReplPower: Boolean = replProps.power -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala b/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala deleted file mode 100644 index 5d386b47b7..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ReplDir.scala +++ /dev/null @@ -1,48 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import io.VirtualDirectory -import settings.MutableSettings -import scala.reflect.io.{ AbstractFile, PlainDirectory, Directory } -import scala.collection.generic.Clearable - -/** Directory to save .class files to. */ -trait ReplDir extends AbstractFile with Clearable { } - -private class ReplVirtualDir() extends VirtualDirectory("(memory)", None) with ReplDir { } -private class ReplRealDir(dir: Directory) extends PlainDirectory(dir) with ReplDir { - def clear() = { - dir.deleteRecursively() - dir.createDirectory() - } -} - -class ReplOutput(val dirSetting: MutableSettings#StringSetting) { - // outdir for generated classfiles - may be in-memory (the default), - // a generated temporary directory, or a specified outdir. - val dir: ReplDir = ( - if (dirSetting.isDefault) - new ReplVirtualDir() - else if (dirSetting.value == "") - new ReplRealDir(Directory.makeTemp("repl")) - else - new ReplRealDir(Directory(dirSetting.value)) - ) - - // print the contents hierarchically - def show(out: JPrintWriter) = { - def pp(root: AbstractFile, indentLevel: Int) { - val label = root.name - val spaces = " " * indentLevel - out.println(spaces + label) - if (root.isDirectory) - root.toList sortBy (_.name) foreach (x => pp(x, indentLevel + 1)) - } - pp(dir, 0) - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala deleted file mode 100644 index 0eabd84234..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ReplGlobal.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import typechecker.Analyzer - -/** A layer on top of Global so I can guarantee some extra - * functionality for the repl. It doesn't do much yet. - */ -trait ReplGlobal extends Global { - // This exists mostly because using the reporter too early leads to deadlock. - private def echo(msg: String) { Console println msg } - - override def abort(msg: String): Nothing = { - echo("ReplGlobal.abort: " + msg) - super.abort(msg) - } - - override lazy val analyzer = new { - val global: ReplGlobal.this.type = ReplGlobal.this - } with Analyzer { - 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) - } - } - } - - override protected def computePhaseDescriptors: List[SubComponent] = { - addToPhasesSet(replPhase, "repl") - super.computePhaseDescriptors - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala b/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala deleted file mode 100644 index 2364918494..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ReplProps.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.sys._ -import Prop._ - -class ReplProps { - private def bool(name: String) = BooleanProp.keyExists(name) - private def int(name: String) = IntProp(name) - - val info = bool("scala.repl.info") - val debug = bool("scala.repl.debug") - val trace = bool("scala.repl.trace") - val power = bool("scala.repl.power") - - val replAutorunCode = Prop[JFile]("scala.repl.autoruncode") - val powerInitCode = Prop[JFile]("scala.repl.power.initcode") - val powerBanner = Prop[JFile]("scala.repl.power.banner") - - val vids = bool("scala.repl.vids") - val maxPrintString = int("scala.repl.maxprintstring") -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala deleted file mode 100644 index b20166d070..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2002-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import reporters._ -import IMain._ - -/** Like ReplGlobal, a layer for ensuring extra functionality. - */ -class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.in, new ReplStrippingWriter(intp)) { - def printUntruncatedMessage(msg: String) = withoutTruncating(printMessage(msg)) - - override def printMessage(msg: String) { - // Avoiding deadlock if the compiler starts logging before - // the lazy val is complete. - if (intp.isInitializeComplete) { - if (intp.totalSilence) { - if (isReplTrace) - super.printMessage("[silent] " + msg) - } - else super.printMessage(msg) - } - else Console.println("[init] " + msg) - } - - override def displayPrompt() { - if (intp.totalSilence) () - else super.displayPrompt() - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala b/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala deleted file mode 100644 index 08472bbc64..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ReplStrings.scala +++ /dev/null @@ -1,32 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.reflect.internal.Chars - -trait ReplStrings { - /** 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 string2codeQuoted(str: String) = - "\"" + string2code(str) + "\"" - - def any2stringOf(x: Any, maxlen: Int) = - "scala.runtime.ScalaRunTime.replStringOf(%s, %s)".format(x, maxlen) - - def words(s: String) = s.trim split "\\s+" filterNot (_ == "") toList -} diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala deleted file mode 100644 index ea100b25f2..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ /dev/null @@ -1,82 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.language.implicitConversions -import scala.reflect.api.{Universe => ApiUniverse} -import scala.reflect.runtime.{universe => ru} - -/** A class which the repl utilizes to expose predefined objects. - * The base implementation is empty; the standard repl implementation - * is StdReplVals. - */ -abstract class ReplVals { } - -class StdReplVals(final val r: ILoop) extends ReplVals { - final lazy val repl = r - final lazy val intp = r.intp - final lazy val power = r.power - final lazy val reader = r.in - final lazy val vals = this - final lazy val global: intp.global.type = intp.global - final lazy val isettings = intp.isettings - final lazy val completion = reader.completion - final lazy val history = reader.history - final lazy val phased = power.phased - final lazy val analyzer = global.analyzer - - object treedsl extends { val global: intp.global.type = intp.global } with ast.TreeDSL { } - - final lazy val typer = analyzer.newTyper( - analyzer.rootContext( - power.unit("").asInstanceOf[analyzer.global.CompilationUnit] - ) - ) - def lastRequest = intp.lastRequest - - class ReplImplicits extends power.Implicits2 { - import intp.global._ - - private val tagFn = ReplVals.mkCompilerTypeFromTag[intp.global.type](global) - implicit def mkCompilerTypeFromTag(sym: Symbol) = tagFn(sym) - } - - final lazy val replImplicits = new ReplImplicits - - def typed[T <: analyzer.global.Tree](tree: T): T = typer.typed(tree).asInstanceOf[T] -} - -object ReplVals { - /** Latest attempt to work around the challenge of foo.global.Type - * not being seen as the same type as bar.global.Type even though - * the globals are the same. Dependent method types to the rescue. - */ - def mkCompilerTypeFromTag[T <: Global](global: T) = { - import global._ - - /** We can't use definitions.compilerTypeFromTag directly because we're passing - * it to map and the compiler refuses to perform eta expansion on a method - * with a dependent return type. (Can this be relaxed?) To get around this - * I have this forwarder which widens the type and then cast the result back - * to the dependent type. - */ - def compilerTypeFromTag(t: ApiUniverse # WeakTypeTag[_]): Global#Type = - definitions.compilerTypeFromTag(t) - - class AppliedTypeFromTags(sym: Symbol) { - def apply[M](implicit m1: ru.TypeTag[M]): Type = - if (sym eq NoSymbol) NoType - else appliedType(sym, compilerTypeFromTag(m1).asInstanceOf[Type]) - - def apply[M1, M2](implicit m1: ru.TypeTag[M1], m2: ru.TypeTag[M2]): Type = - if (sym eq NoSymbol) NoType - else appliedType(sym, compilerTypeFromTag(m1).asInstanceOf[Type], compilerTypeFromTag(m2).asInstanceOf[Type]) - } - - (sym: Symbol) => new AppliedTypeFromTags(sym) - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/Results.scala b/src/compiler/scala/tools/nsc/interpreter/Results.scala deleted file mode 100644 index e400906a58..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/Results.scala +++ /dev/null @@ -1,22 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package interpreter - -object Results { - /** A result from the Interpreter interpreting one line of input. */ - abstract sealed class Result - - /** The line was interpreted successfully. */ - case object Success extends Result - - /** The line was erroneous in some way. */ - case object Error extends Result - - /** The input was incomplete. The caller should request more input. - */ - case object Incomplete extends Result -} diff --git a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala deleted file mode 100644 index 36cdf65510..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import scala.reflect.{ ClassTag, classTag } - -class RichClass[T](val clazz: Class[T]) { - def toTag: ClassTag[T] = ClassTag[T](clazz) - - // Sadly isAnonymousClass does not return true for scala anonymous - // classes because our naming scheme is not doing well against the - // jvm's many assumptions. - def isScalaAnonymous = ( - try clazz.isAnonymousClass || (clazz.getName contains "$anon$") - catch { case _: java.lang.InternalError => false } // good ol' "Malformed class name" - ) - - def supertags: List[ClassTag[_]] = supers map (_.toTag) - def superNames: List[String] = supers map (_.getName) - def interfaces: List[JClass] = supers filter (_.isInterface) - - def hasAncestorName(f: String => Boolean) = superNames exists f - def hasAncestor(f: JClass => Boolean) = supers exists f - - def supers: List[JClass] = { - def loop(x: JClass): List[JClass] = x.getSuperclass match { - case null => List(x) - case sc => x :: (x.getInterfaces.toList flatMap loop) ++ loop(sc) - } - loop(clazz).distinct - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala b/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala deleted file mode 100644 index 2d0917d91f..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/SimpleReader.scala +++ /dev/null @@ -1,40 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Stepan Koltsov - */ - -package scala.tools.nsc -package interpreter - -import java.io.{ BufferedReader } -import session.NoHistory - -/** Reads using standard JDK API */ -class SimpleReader( - in: BufferedReader, - out: JPrintWriter, - val interactive: Boolean) -extends InteractiveReader -{ - val history = NoHistory - val completion = NoCompletion - - def reset() = () - def redrawLine() = () - def readOneLine(prompt: String): String = { - if (interactive) { - out.print(prompt) - out.flush() - } - in.readLine() - } - def readOneKey(prompt: String) = sys.error("No char-based input in SimpleReader") -} - -object SimpleReader { - def defaultIn = Console.in - def defaultOut = new JPrintWriter(Console.out) - - def apply(in: BufferedReader = defaultIn, out: JPrintWriter = defaultOut, interactive: Boolean = true): SimpleReader = - new SimpleReader(in, out, interactive) -} diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala deleted file mode 100644 index 52a085080b..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/package.scala +++ /dev/null @@ -1,157 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc - -import scala.language.implicitConversions -import scala.reflect.{ classTag, ClassTag } -import scala.reflect.runtime.{ universe => ru } -import scala.reflect.{ClassTag, classTag} -import scala.reflect.api.{Mirror, TypeCreator, Universe => ApiUniverse} - -/** The main REPL related classes and values are as follows. - * In addition to standard compiler classes Global and Settings, there are: - * - * History: an interface for session history. - * Completion: an interface for tab completion. - * ILoop (formerly InterpreterLoop): The umbrella class for a session. - * IMain (formerly Interpreter): Handles the evolving state of the session - * and handles submitting code to the compiler and handling the output. - * InteractiveReader: how ILoop obtains input. - * History: an interface for session history. - * Completion: an interface for tab completion. - * Power: a repository for more advanced/experimental features. - * - * ILoop contains { in: InteractiveReader, intp: IMain, settings: Settings, power: Power } - * InteractiveReader contains { history: History, completion: Completion } - * IMain contains { global: Global } - */ -package object interpreter extends ReplConfig with ReplStrings { - type JFile = java.io.File - type JClass = java.lang.Class[_] - type JList[T] = java.util.List[T] - type JCollection[T] = java.util.Collection[T] - type JPrintWriter = java.io.PrintWriter - type InputStream = java.io.InputStream - type OutputStream = java.io.OutputStream - - val IR = Results - - implicit def postfixOps = scala.language.postfixOps // make all postfix ops in this package compile without warning - - private[interpreter] implicit def javaCharSeqCollectionToScala(xs: JCollection[_ <: CharSequence]): List[String] = { - import scala.collection.JavaConverters._ - xs.asScala.toList map ("" + _) - } - - private[nsc] implicit def enrichClass[T](clazz: Class[T]) = new RichClass[T](clazz) - private[nsc] implicit def enrichAnyRefWithTap[T](x: T) = new TapMaker(x) - private[nsc] def debugging[T](msg: String)(x: T) = x.tapDebug(msg) - - private val ourClassloader = getClass.getClassLoader - - def staticTypeTag[T: ClassTag]: ru.TypeTag[T] = ru.TypeTag[T]( - ru.runtimeMirror(ourClassloader), - new TypeCreator { - def apply[U <: ApiUniverse with Singleton](m: Mirror[U]): U # Type = - m.staticClass(classTag[T].runtimeClass.getName).toTypeConstructor.asInstanceOf[U # Type] - }) - - /** This class serves to trick the compiler into treating a var - * (intp, in ILoop) as a stable identifier. - */ - implicit class IMainOps(val intp: IMain) { - import intp._ - import global.{ reporter => _, _ } - import definitions._ - - protected def echo(msg: String) = { - Console.out println msg - Console.out.flush() - } - - def implicitsCommand(line: String): String = { - def p(x: Any) = intp.reporter.printMessage("" + x) - - // If an argument is given, only show a source with that - // in its name somewhere. - val args = line split "\\s+" - val filtered = intp.implicitSymbolsBySource filter { - case (source, syms) => - (args contains "-v") || { - if (line == "") (source.fullName.toString != "scala.Predef") - else (args exists (source.name.toString contains _)) - } - } - - 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 + " */") - - // This groups the members by where the symbol is defined - val byOwner = syms groupBy (_.owner) - val sortedOwners = byOwner.toList sortBy { case (owner, _) => exitingTyper(source.info.baseClasses indexOf owner) } - - sortedOwners foreach { - case (owner, members) => - // Within each owner, we cluster results based on the final result type - // if there are more than a couple, and sort each cluster based on name. - // This is really just trying to make the 100 or so implicits imported - // by default into something readable. - val memberGroups: List[List[Symbol]] = { - val groups = members groupBy (_.tpe.finalResultType) toList - val (big, small) = groups partition (_._2.size > 3) - val xss = ( - (big sortBy (_._1.toString) map (_._2)) :+ - (small flatMap (_._2)) - ) - - xss map (xs => xs sortBy (_.name.toString)) - } - - val ownerMessage = if (owner == source) " defined in " else " inherited from " - p(" /* " + members.size + ownerMessage + owner.fullName + " */") - - memberGroups foreach { group => - group foreach (s => p(" " + intp.symbolDefString(s))) - p("") - } - } - p("") - } - "" - } - - /** TODO - - * -n normalize - * -l label with case class parameter names - * -c complete - leave nothing out - */ - def typeCommandInternal(expr: String, verbose: Boolean): Unit = - symbolOfLine(expr) andAlso (echoTypeSignature(_, verbose)) - - def printAfterTyper(msg: => String) = - reporter printUntruncatedMessage exitingTyper(msg) - - private def replInfo(sym: Symbol) = - if (sym.isAccessor) dropNullaryMethod(sym.info) else sym.info - - def echoTypeStructure(sym: Symbol) = - printAfterTyper("" + deconstruct.show(replInfo(sym))) - - def echoTypeSignature(sym: Symbol, verbose: Boolean) = { - if (verbose) echo("// Type signature") - printAfterTyper("" + replInfo(sym)) - - if (verbose) { - echo("\n// Internal Type structure") - echoTypeStructure(sym) - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala b/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala deleted file mode 100644 index dddfb1b8f6..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/session/FileBackedHistory.scala +++ /dev/null @@ -1,84 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter -package session - -import scala.tools.nsc.io._ -import FileBackedHistory._ - -/** TODO: file locking. - */ -trait FileBackedHistory extends JLineHistory with JPersistentHistory { - def maxSize: Int - protected lazy val historyFile: File = defaultFile - private var isPersistent = true - - locally { - load() - } - - def withoutSaving[T](op: => T): T = { - val saved = isPersistent - isPersistent = false - try op - finally isPersistent = saved - } - def addLineToFile(item: CharSequence): Unit = { - if (isPersistent) - append(item + "\n") - } - - /** Overwrites the history file with the current memory. */ - protected def sync(): Unit = { - val lines = asStrings map (_ + "\n") - historyFile.writeAll(lines: _*) - } - /** Append one or more lines to the history file. */ - protected def append(lines: String*): Unit = { - historyFile.appendAll(lines: _*) - } - - def load(): Unit = { - if (!historyFile.canRead) - historyFile.createFile() - - val lines: IndexedSeq[String] = { - try historyFile.lines().toIndexedSeq - catch { - // It seems that control characters in the history file combined - // with the default codec can lead to nio spewing exceptions. Rather - // than abandon hope we'll try to read it as ISO-8859-1 - case _: Exception => - try historyFile.lines("ISO-8859-1").toIndexedSeq - catch { case _: Exception => Vector() } - } - } - - repldbg("Loading " + lines.size + " into history.") - - // avoid writing to the history file - withoutSaving(lines takeRight maxSize foreach add) - // truncate the history file if it's too big. - if (lines.size > maxSize) { - repldbg("File exceeds maximum size: truncating to " + maxSize + " entries.") - sync() - } - moveToEnd() - } - - def flush(): Unit = () - def purge(): Unit = historyFile.truncate() -} - -object FileBackedHistory { - // val ContinuationChar = '\003' - // val ContinuationNL: String = Array('\003', '\n').mkString - import Properties.userHome - - def defaultFileName = ".scala_history" - def defaultFile: File = File(Path(userHome) / defaultFileName) -} diff --git a/src/compiler/scala/tools/nsc/interpreter/session/History.scala b/src/compiler/scala/tools/nsc/interpreter/session/History.scala deleted file mode 100644 index 794d41adc7..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/session/History.scala +++ /dev/null @@ -1,22 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter -package session - -/** An implementation-agnostic history interface which makes no - * reference to the jline classes. Very sparse right now. - */ -trait History { - def asStrings: List[String] - def index: Int - def size: Int -} -object NoHistory extends History { - def asStrings = Nil - def index = 0 - def size = 0 -} diff --git a/src/compiler/scala/tools/nsc/interpreter/session/JLineHistory.scala b/src/compiler/scala/tools/nsc/interpreter/session/JLineHistory.scala deleted file mode 100644 index 18e0ee7c85..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/session/JLineHistory.scala +++ /dev/null @@ -1,49 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter -package session - -/** A straight scalification of the jline interface which mixes - * in the sparse jline-independent one too. - */ -trait JLineHistory extends JHistory with History { - def size: Int - def isEmpty: Boolean - def index: Int - def clear(): Unit - def get(index: Int): CharSequence - def add(line: CharSequence): Unit - def replace(item: CharSequence): Unit - - def entries(index: Int): JListIterator[JEntry] - def entries(): JListIterator[JEntry] - def iterator: JIterator[JEntry] - - def current(): CharSequence - def previous(): Boolean - def next(): Boolean - def moveToFirst(): Boolean - def moveToLast(): Boolean - def moveTo(index: Int): Boolean - def moveToEnd(): Unit -} - -object JLineHistory { - class JLineFileHistory extends SimpleHistory with FileBackedHistory { - override def add(item: CharSequence): Unit = { - if (!isEmpty && last == item) - repldbg("Ignoring duplicate entry '" + item + "'") - else { - super.add(item) - addLineToFile(item) - } - } - override def toString = "History(size = " + size + ", index = " + index + ")" - } - - def apply(): JLineHistory = try new JLineFileHistory catch { case x: Exception => new SimpleHistory() } -} diff --git a/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala b/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala deleted file mode 100644 index 89998e438a..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/session/SimpleHistory.scala +++ /dev/null @@ -1,58 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter -package session - -import scala.collection.mutable.{ Buffer, ListBuffer } -import scala.collection.JavaConverters._ - -class SimpleHistory extends JLineHistory { - private var _index: Int = 0 - private val buf: Buffer[String] = new ListBuffer[String] - private def toEntries(): Seq[JEntry] = buf.zipWithIndex map { case (x, i) => Entry(i, x) } - private def setTo(num: Int) = { _index = num ; true } - private def minusOne = { _index -= 1 ; true } - private def plusOne = { _index += 1 ; true } - private def lastIndex = size - 1 - private def fail(msg: String): String = { - repldbg("Internal error in history(size %d, index %d): %s".format( - size, index, msg) - ) - "" - } - - case class Entry(index: Int, value: CharSequence) extends JEntry { - override def toString = value - } - - def maxSize: Int = 2500 - def last = if (isEmpty) fail("last") else buf.last - - def size = buf.size - def index = _index - def isEmpty = buf.isEmpty - def clear() = buf.clear() - def get(idx: Int): CharSequence = buf(idx) - def add(item: CharSequence): Unit = buf += item - def replace(item: CharSequence): Unit = { - buf trimEnd 1 - add(item) - } - def entries(idx: Int): JListIterator[JEntry] = toEntries().asJava.listIterator(idx) - def entries(): JListIterator[JEntry] = toEntries().asJava.listIterator() - def iterator: JIterator[JEntry] = toEntries().iterator.asJava - - def current() = if (index >= 0 && index < buf.size) buf(index) else fail("current()") - def previous() = (index > 0) && minusOne - def next() = (index <= lastIndex) && plusOne - def moveToFirst() = (size > 0) && (index != 0) && setTo(0) - def moveToLast() = (size > 0) && (index < lastIndex) && setTo(lastIndex) - def moveTo(idx: Int) = (idx > 0) && (idx <= lastIndex) && setTo(idx) - def moveToEnd(): Unit = setTo(size) - - def asStrings = buf.toList -} diff --git a/src/compiler/scala/tools/nsc/interpreter/session/package.scala b/src/compiler/scala/tools/nsc/interpreter/session/package.scala deleted file mode 100644 index c62cf21151..0000000000 --- a/src/compiler/scala/tools/nsc/interpreter/session/package.scala +++ /dev/null @@ -1,23 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter -import scala.language.implicitConversions - -/** Files having to do with the state of a repl session: - * lines of text entered, types and terms defined, etc. - */ -package object session { - type JIterator[T] = java.util.Iterator[T] - type JListIterator[T] = java.util.ListIterator[T] - - type JEntry = scala.tools.jline.console.history.History.Entry - type JHistory = scala.tools.jline.console.history.History - type JMemoryHistory = scala.tools.jline.console.history.MemoryHistory - type JPersistentHistory = scala.tools.jline.console.history.PersistentHistory - - private[interpreter] implicit def charSequenceFix(x: CharSequence): String = x.toString -} diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 2aee9bd4bc..9469113238 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -188,7 +188,6 @@ trait ScalaSettings extends AbsScalaSettings val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.") val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.") val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.") - val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.") andThen (interpreter.replProps.debug setValue _) val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.") val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.") diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 1693bdbc8c..91ebd798e1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -43,8 +43,15 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { import definitions._ import treeInfo.{isRepeatedParamType => _, _} import MacrosStats._ + def globalSettings = global.settings + protected def findMacroClassLoader(): ClassLoader = { + val classpath = global.classPath.asURLs + macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath)) + ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + } + /** `MacroImplBinding` and its companion module are responsible for * serialization/deserialization of macro def -> impl bindings. * @@ -474,21 +481,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { * Loads classes from from -cp (aka the library classpath). * Is also capable of detecting REPL and reusing its classloader. */ - lazy val macroClassloader: ClassLoader = { - val classpath = global.classPath.asURLs - macroLogVerbose("macro classloader: initializing from -cp: %s".format(classpath)) - val loader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) - - // a heuristic to detect the REPL - if (global.settings.exposeEmptyPackage.value) { - macroLogVerbose("macro classloader: initializing from a REPL classloader: %s".format(global.classPath.asURLs)) - import scala.tools.nsc.interpreter._ - val virtualDirectory = global.settings.outputDirs.getSingleOutput.get - new AbstractFileClassLoader(virtualDirectory, loader) {} - } else { - loader - } - } + lazy val macroClassloader: ClassLoader = findMacroClassLoader() /** Produces a function that can be used to invoke macro implementation for a given macro definition: * 1) Looks up macro implementation symbol in this universe. diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala index 3ecd3b9ae4..65a3fedbd2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala @@ -4,7 +4,7 @@ */ package scala.tools.nsc -package interpreter +package typechecker import java.lang.{ reflect => r } import r.TypeVariable @@ -12,7 +12,6 @@ import scala.reflect.NameTransformer import NameTransformer._ import scala.reflect.runtime.{universe => ru} import scala.reflect.{ClassTag, classTag} -import typechecker.DestructureTypes /** A more principled system for turning types into strings. */ @@ -53,8 +52,7 @@ trait StructuredTypeStrings extends DestructureTypes { private def shortClass(x: Any) = { if (settings.debug.value) { val name = (x.getClass.getName split '.').last - val isAnon = name.reverse takeWhile (_ != '$') forall (_.isDigit) - val str = if (isAnon) name else (name split '$').last + val str = if (TypeStrings.isAnonClass(x.getClass)) name else (name split '$').last " // " + str } @@ -152,11 +150,11 @@ trait StructuredTypeStrings extends DestructureTypes { * "definition" is when you want strings like */ trait TypeStrings { + private type JClass = java.lang.Class[_] private val ObjectClass = classOf[java.lang.Object] private val primitives = Set[String]("byte", "char", "short", "int", "long", "float", "double", "boolean", "void") private val primitiveMap = primitives.toList map { x => val key = x match { - case "void" => "Void" case "int" => "Integer" case "char" => "Character" case s => s.capitalize @@ -169,6 +167,11 @@ trait TypeStrings { ("java.lang." + key) -> ("scala." + value) } toMap + def isAnonClass(cl: Class[_]) = { + val xs = cl.getName.reverse takeWhile (_ != '$') + xs.nonEmpty && xs.forall(_.isDigit) + } + def scalaName(s: String): String = { if (s endsWith MODULE_SUFFIX_STRING) s.init + ".type" else if (s == "void") "scala.Unit" @@ -178,17 +181,16 @@ trait TypeStrings { // Trying to put humpty dumpty back together again. def scalaName(clazz: JClass): String = { val name = clazz.getName - val isAnon = clazz.isScalaAnonymous val enclClass = clazz.getEnclosingClass def enclPre = enclClass.getName + MODULE_SUFFIX_STRING def enclMatch = name startsWith enclPre scalaName( - if (enclClass == null || isAnon || !enclMatch) name + if (enclClass == null || isAnonClass(clazz) || !enclMatch) name else enclClass.getName + "." + (name stripPrefix enclPre) ) } - def anyClass(x: Any): JClass = if (x == null) null else x.getClass + def anyClass(x: Any): JClass = if (x == null) null else x.getClass private def brackets(tps: String*): String = if (tps.isEmpty) "" diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/util/AbstractFileClassLoader.scala index e909cd945d..7aef87f387 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/util/AbstractFileClassLoader.scala @@ -3,10 +3,9 @@ */ package scala.tools.nsc -package interpreter +package util import scala.tools.nsc.io.AbstractFile -import util.ScalaClassLoader import java.net.{ URL, URLConnection, URLStreamHandler } import scala.collection.{ mutable, immutable } @@ -78,7 +77,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) case null => super.classBytes(name) case file => file.toByteArray } - override def findClass(name: String): JClass = { + override def findClass(name: String): Class[_] = { val bytes = classBytes(name) if (bytes.length == 0) throw new ClassNotFoundException(name) diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala index 4bc393bd0b..f91e94471a 100644 --- a/src/compiler/scala/tools/nsc/util/ShowPickled.scala +++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala @@ -13,7 +13,6 @@ import java.lang.Float.intBitsToFloat import java.lang.Double.longBitsToDouble import scala.reflect.internal.{Flags, Names} import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } -import interpreter.ByteCode.scalaSigBytesForPath object ShowPickled extends Names { import PickleFormat._ @@ -272,7 +271,6 @@ object ShowPickled extends Names { } def fromFile(path: String) = fromBytes(io.File(path).toByteArray()) - def fromName(name: String) = fromBytes(scalaSigBytesForPath(name) getOrElse Array()) def fromBytes(data: => Array[Byte]): Option[PickleBuffer] = try Some(new PickleBuffer(data, 0, data.length)) catch { case _: Exception => None } @@ -287,7 +285,7 @@ object ShowPickled extends Names { def main(args: Array[String]) { args foreach { arg => - (fromFile(arg) orElse fromName(arg)) match { + fromFile(arg) match { case Some(pb) => show(arg + ":", pb) case _ => Console.println("Cannot read " + arg) } diff --git a/src/compiler/scala/tools/reflect/StdTags.scala b/src/compiler/scala/tools/reflect/StdTags.scala index 5c62819f04..6c1821f8aa 100644 --- a/src/compiler/scala/tools/reflect/StdTags.scala +++ b/src/compiler/scala/tools/reflect/StdTags.scala @@ -23,7 +23,7 @@ trait StdTags { } }) - private def tagOfStaticClass[T: ClassTag]: u.TypeTag[T] = + protected def tagOfStaticClass[T: ClassTag]: u.TypeTag[T] = u.TypeTag[T]( m, new TypeCreator { @@ -34,8 +34,6 @@ trait StdTags { lazy val tagOfString = tagOfStaticClass[String] lazy val tagOfFile = tagOfStaticClass[scala.tools.nsc.io.File] lazy val tagOfDirectory = tagOfStaticClass[scala.tools.nsc.io.Directory] - lazy val tagOfStdReplVals = tagOfStaticClass[scala.tools.nsc.interpreter.StdReplVals] - lazy val tagOfIMain = tagOfStaticClass[scala.tools.nsc.interpreter.IMain] lazy val tagOfThrowable = tagOfStaticClass[java.lang.Throwable] lazy val tagOfClassLoader = tagOfStaticClass[java.lang.ClassLoader] lazy val tagOfBigInt = tagOfStaticClass[BigInt] diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3bde280681..e6bbe1dbed 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -5,7 +5,7 @@ import scala.tools.nsc.EXPRmode import scala.tools.nsc.reporters._ import scala.tools.nsc.CompilerCommand import scala.tools.nsc.io.VirtualDirectory -import scala.tools.nsc.interpreter.AbstractFileClassLoader +import scala.tools.nsc.util.AbstractFileClassLoader import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} import java.lang.{Class => jClass} diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala index 7e984fd1f7..3cfc1eb2a1 100644 --- a/src/compiler/scala/tools/util/Javap.scala +++ b/src/compiler/scala/tools/util/Javap.scala @@ -6,27 +6,14 @@ package scala.tools package util -import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } import scala.tools.nsc.util.ScalaClassLoader -import scala.tools.nsc.interpreter.IMain -import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, InputStream, - PrintWriter, Writer } -import java.util.{ Locale } -import java.util.regex.Pattern -import javax.tools.{ Diagnostic, DiagnosticCollector, DiagnosticListener, - ForwardingJavaFileManager, JavaFileManager, JavaFileObject, - SimpleJavaFileObject, StandardLocation } -import scala.reflect.io.{ AbstractFile, Directory, File, Path } -import java.io.{File => JFile} -import scala.io.Source -import scala.util.{ Try, Success, Failure } -import scala.util.Properties.lineSeparator -import scala.collection.JavaConverters -import scala.collection.generic.Clearable -import java.net.URL -import scala.language.reflectiveCalls +import java.io.PrintWriter -import Javap._ +trait JpResult { + def isError: Boolean + def value: Any + def show(): Unit +} trait Javap { def loader: ScalaClassLoader @@ -43,672 +30,3 @@ object NoJavap extends Javap { def tryFile(path: String): Option[Array[Byte]] = None def tryClass(path: String): Array[Byte] = Array() } - -class JavapClass( - val loader: ScalaClassLoader, - val printWriter: PrintWriter, - intp: Option[IMain] = None -) extends Javap { - import JavapTool.ToolArgs - import JavapClass._ - - lazy val tool = JavapTool() - - /** Run the tool. Option args start with "-". - * The default options are "-protected -verbose". - * Byte data for filename args is retrieved with findBytes. - */ - def apply(args: Seq[String]): List[JpResult] = { - val (options, claases) = 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(claases) else claases - if (help || claases.isEmpty) List(JpResult(JavapTool.helper(printWriter))) - else if (targets.isEmpty) List(JpResult("No anonfuns found.")) - else tool(raw, upgraded)(targets map (claas => claas -> bytesFor(claas, app))) - } - - /** 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) - } - - /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). */ - private def bytesFor(path: String, app: Boolean) = Try { - def last = intp.get.mostRecentVar // fail if no intp - def req = if (path == "-") last else { - val s = path.splitHashMember - if (s._1.nonEmpty) s._1 - else s._2 getOrElse "#" - } - def asAppBody(s: String) = { - val (cls, fix) = s.splitSuffix - s"${cls}$$delayedInit$$body${fix}" - } - def todo = if (app) asAppBody(req) else req - val bytes = findBytes(todo) - if (bytes.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '${path}'") - else bytes - } - - 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. - */ - def tryFile(path: String): Option[Array[Byte]] = - (Try (File(path.asClassResource)) filter (_.exists) map (_.toByteArray())).toOption - - /** Assume the string is a fully qualified class name and try to - * find the class object it represents. - * There are other symbols of interest, too: - * - a definition that is wrapped in an enclosing class - * - a synthetic that is not in scope but its associated class is - */ - def tryClass(path: String): Array[Byte] = { - def load(name: String) = loader classBytes name - def loadable(name: String) = loader resourceable name - // if path has an interior dollar, take it as a synthetic - // if the prefix up to the dollar is a symbol in scope, - // result is the translated prefix + suffix - def desynthesize(s: String) = { - val i = s indexOf '$' - 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) - def loadableOrNone(strip: Boolean) = { - def suffix(strip: Boolean)(x: String) = - (if (strip && (x endsWith "$")) x.init else x) + sufx - val res = tran map (suffix(strip) _) - if (res.isDefined && loadable(res.get)) res else None - } - // try loading translated+suffix - val res = loadableOrNone(strip = false) - // some synthetics lack a dollar, (e.g., suffix = delayedInit$body) - // so as a hack, if prefix$$suffix fails, also try prefix$suffix - if (res.isDefined) res else loadableOrNone(strip = true) - } else None - } - val p = path.asClassName // scrub any suffix - // 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 ( - // 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) - // take path as a Name in scope and find its enclosing class - orElse (intp flatMap (_ translateEnclosingClass p) filter loadable) - // take path as a synthetic derived from some Name in scope - orElse desynthesize(p) - // just try it plain - getOrElse p - ) - load(q) - } - - /** Base class for javap tool adapters for java 6 and 7. */ - abstract class JavapTool { - type ByteAry = Array[Byte] - type Input = Pair[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 - } - protected def noToolError = new JpError(s"No javap tool available: ${getClass.getName} failed to initialize.") - - // output filtering support - val writer = new CharArrayWriter - def written = { - writer.flush() - val w = writer.toString - writer.reset() - w - } - - /** 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 { - // 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 { writeLines() } - else writeLines() - private def writeLines() { - // 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#? - val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } - var filtering = false // true if in region matching filter - // true to output - def checkFilter(line: String) = if (filterOn.isEmpty) true else { - // cheap heuristic, todo maybe parse for the java sig. - // method sigs end in paren semi - def isAnyMethod = line.endsWith(");") - def isOurMethod = { - val lparen = line.lastIndexOf('(') - val blank = line.lastIndexOf(' ', lparen) - (blank >= 0 && line.substring(blank+1, lparen) == filterOn.get) - } - filtering = if (filtering) { - // next blank line terminates section - // for -public, next line is next method, more or less - line.trim.nonEmpty && !isAnyMethod - } else { - isAnyMethod && isOurMethod - } - filtering - } - for (line <- Source.fromString(preamble + written).getLines(); if checkFilter(line)) - printWriter write line+lineSeparator - printWriter.flush() - } - } - } - - 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 (claas, Success(ba)) => JpResult(showable(raw, claas, 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 - //def run(args: Array[String]): Int // all args - //def handleOptions(args: Array[String]): Unit // options, then run() or call() - } - // result of Task.run - //object TaskResult extends Enumeration { - // val Ok, Error, CmdErr, SysErr, Abnormal = Value - //} - val TaskClaas = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull - override protected def failed = TaskClaas eq null - - val TaskCtor = TaskClaas.getConstructor( - classOf[Writer], - classOf[JavaFileManager], - classOf[DiagnosticListener[_]], - classOf[JIterable[String]], - classOf[JIterable[String]] - ) orFailed null - - class JavaReporter extends DiagnosticListener[JavaFileObject] with Clearable { - import scala.collection.mutable.{ ArrayBuffer, SynchronizedBuffer } - type D = Diagnostic[_ <: JavaFileObject] - val diagnostics = new ArrayBuffer[D] with SynchronizedBuffer[D] - override def report(d: Diagnostic[_ <: JavaFileObject]) { - diagnostics += d - } - override def clear() = diagnostics.clear() - /** All diagnostic messages. - * @param locale Locale for diagnostic messages, null by default. - */ - def messages(implicit locale: Locale = null) = (diagnostics map (_ getMessage locale)).toList - - def reportable(raw: Boolean): String = { - // don't filter this message if raw, since the names are likely to differ - val container = "Binary file .* contains .*".r - val m = if (raw) messages - else messages filter (_ match { case container() => false case _ => true }) - clear() - if (m.nonEmpty) m mkString ("", lineSeparator, lineSeparator) - else "" - } - } - val reporter = new JavaReporter - - // DisassemblerTool.getStandardFileManager(reporter,locale,charset) - val defaultFileManager: JavaFileManager = - (loader.tryToLoadClass[JavaFileManager]("com.sun.tools.javap.JavapFileManager").get getMethod ( - "create", - classOf[DiagnosticListener[_]], - classOf[PrintWriter] - ) invoke (null, reporter, new PrintWriter(System.err, true))).asInstanceOf[JavaFileManager] orFailed null - - // manages named arrays of bytes, which might have failed to load - class JavapFileManager(val managed: Seq[Input])(delegate: JavaFileManager = defaultFileManager) - extends ForwardingJavaFileManager[JavaFileManager](delegate) { - import JavaFileObject.Kind - import Kind._ - import StandardLocation._ - import JavaFileManager.Location - import java.net.URI - def uri(name: String): URI = new URI(name) // new URI("jfo:" + name) - - def inputNamed(name: String): Try[ByteAry] = (managed find (_._1 == name)).get._2 - def managedFile(name: String, kind: Kind) = kind match { - case CLASS => fileObjectForInput(name, inputNamed(name), kind) - case _ => null - } - // todo: just wrap it as scala abstractfile and adapt it uniformly - def fileObjectForInput(name: String, bytes: Try[ByteAry], kind: Kind): JavaFileObject = - new SimpleJavaFileObject(uri(name), kind) { - override def openInputStream(): InputStream = new ByteArrayInputStream(bytes.get) - // if non-null, ClassWriter wrongly requires scheme non-null - override def toUri: URI = null - override def getName: String = name - // suppress - override def getLastModified: Long = -1L - } - override def getJavaFileForInput(location: Location, className: String, kind: Kind): JavaFileObject = - location match { - case CLASS_PATH => managedFile(className, kind) - case _ => null - } - override def hasLocation(location: Location): Boolean = - location match { - case CLASS_PATH => true - case _ => false - } - } - 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)) - - // eventually, use the tool interface - def task(options: Seq[String], claases: Seq[String], inputs: Seq[Input]): Task = { - //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). - //getTask(writer, fileManager, reporter, options.asJava, claases.asJava) - import JavaConverters.asJavaIterableConverter - TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, claases.asJava) - .orFailed (throw new IllegalStateException) - } - // a result per input - private def applyOne(raw: Boolean, options: Seq[String], claas: String, inputs: Seq[Input]): Try[JpResult] = - Try { - task(options, Seq(claas), inputs).call() - } map { - case true => JpResult(showable(raw, claas)) - case _ => JpResult(reporter.reportable(raw)) - } recoverWith { - case e: java.lang.reflect.InvocationTargetException => e.getCause match { - case t: IllegalArgumentException => Success(JpResult(t.getMessage)) // bad option - case x => Failure(x) - } - } lastly { - reporter.clear() - } - override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { - case (claas, Success(_)) => applyOne(raw, options, claas, 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) - 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") - - def isAvailable = Seq(Env, Tool) exists (cn => hasClass(loader, cn)) - - private def hasClass(cl: ScalaClassLoader, cn: String) = cl.tryToInitializeClass[AnyRef](cn).isDefined - - private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) - - def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 - } -} - -object JavapClass { - def apply( - loader: ScalaClassLoader = ScalaClassLoader.appLoader, - printWriter: PrintWriter = new PrintWriter(System.out, true), - intp: Option[IMain] = None - ) = new JavapClass(loader, printWriter, intp) - - // We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget - // or a resource path com/acme/Widget.class; but not widget.out - implicit class MaybeClassLike(val s: String) extends AnyVal { - /* private[this] final val suffix = ".class" */ - private def suffix = ".class" - def asClassName = (s stripSuffix suffix).replace('/', '.') - def asClassResource = if (s endsWith suffix) s else s.replace('.', '/') + suffix - def splitSuffix: (String, String) = if (s endsWith suffix) (s dropRight suffix.length, suffix) else (s, "") - def strippingSuffix(f: String => String): String = - if (s endsWith suffix) f(s dropRight suffix.length) else s - // e.g. Foo#bar. Foo# yields zero-length member part. - def splitHashMember: (String, Option[String]) = { - val i = s lastIndexOf '#' - if (i < 0) (s, None) - //else if (i >= s.length - 1) (s.init, None) - else (s take i, Some(s drop i+1)) - } - } - implicit class ClassLoaderOps(val cl: ClassLoader) extends AnyVal { - private def parentsOf(x: ClassLoader): List[ClassLoader] = if (x == null) Nil else x :: parentsOf(x.getParent) - def parents: List[ClassLoader] = parentsOf(cl) - /* all file locations */ - def locations = { - def alldirs = parents flatMap (_ match { - 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 claas = try cl loadClass k catch { - case _: NoClassDefFoundError => null // let it snow - } - // cf ScalaClassLoader.originOfClass - claas.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 = cl.getResource(className.asClassResource) != null - } - implicit class PathOps(val p: Path) extends AnyVal { - import scala.tools.nsc.io.Jar - def isJar = Jar isJarOrZip p - } - 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) - } - class FunFinder(loader: ScalaClassLoader, intp: Option[IMain]) { - - // class k, candidate f without prefix - def isFunOfClass(k: String, f: String) = { - val p = (s"${Pattern quote k}\\$$+anonfun").r - (p findPrefixOf f).nonEmpty - } - // class k, candidate f without prefix, method m - def isFunOfMethod(k: String, m: String, f: String) = { - val p = (s"${Pattern quote k}\\$$+anonfun\\$$${Pattern quote m}\\$$").r - (p findPrefixOf f).nonEmpty - } - def isFunOfTarget(k: String, m: Option[String], f: String) = - if (m.isEmpty) isFunOfClass(k, f) - else isFunOfMethod(k, m.get, f) - def listFunsInAbsFile(k: String, m: Option[String], d: AbstractFile) = { - for (f <- d; if !f.isDirectory && isFunOfTarget(k, m, f.name)) yield f.name - } - // path prefix p, class k, dir d - def listFunsInDir(p: String, k: String, m: Option[String])(d: Directory) = { - val subdir = Path(p) - for (f <- (d / subdir).toDirectory.list; if f.isFile && isFunOfTarget(k, m, f.name)) - yield f.name - } - // path prefix p, class k, jar file f - def listFunsInJar(p: String, k: String, m: Option[String])(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 == p && isFunOfTarget(k, m, name)) Some(name) else None - } - (new Jar(f) map maybe).flatten - } - def loadable(name: String) = loader resourceable name - // translated class, optional member, opt member to filter on, whether it is repl output - def translate(s: String): (String, Option[String], Option[String], Boolean) = { - val (k0, m0) = s.splitHashMember - val k = k0.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 ((_, member, filter, true))) - // 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 ((_, Some(k), filter, true))) - getOrElse (k, member, filter, false)) - } - /** Find the classnames of anonfuns associated with k, - * where k may be an available class or a symbol in scope. - */ - def funsOf(k0: String): Seq[String] = { - // class is either something replish or available to loader - val (k, member, filter, isReplish) = translate(k0) - val splat = k 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 "" - // 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 (pkg.isEmpty) s else s"$pkg.$s" - val pm = filter map (p + "#" + _) - pm getOrElse p - } - // is this translated path in (usually virtual) repl outdir? or loadable from filesystem? - val fs = if (isReplish) { - def outed(d: AbstractFile, p: Seq[String]): Option[AbstractFile] = { - if (p.isEmpty) Option(d) - else Option(d.lookupName(p.head, directory = true)) flatMap (f => outed(f, p.tail)) - } - outed(intp.get.replOutput.dir, splat.init) map { d => - listFunsInAbsFile(name, member, d) map packaged - } - } else { - loader locate k map { w => - if (w.isDirectory) listFunsInDir(prefix, name, member)(w.toDirectory) map packaged - else if (w.isJar) listFunsInJar(prefix, name, member)(w.toFile) map packaged - else Nil - } - } - fs match { - case Some(xs) => xs.to[Seq] // maybe empty - case None => Seq() // nothing found, e.g., junk input - } - } - def funs(ks: Seq[String]) = ks flatMap funsOf _ - } -} - -object Javap { - - def isAvailable(cl: ScalaClassLoader = ScalaClassLoader.appLoader) = JavapClass(cl).JavapTool.isAvailable - - def apply(path: String): Unit = apply(Seq(path)) - def apply(args: Seq[String]): Unit = JavapClass() apply args foreach (_.show()) - - trait Showable { - def show(): Unit - } - - sealed trait JpResult { - type ResultType - def isError: Boolean - def value: ResultType - def show(): Unit - // todo - // def header(): String - // def fields(): List[String] - // def methods(): List[String] - // def signatures(): List[String] - } - object JpResult { - def apply(msg: String) = new JpError(msg) - def apply(res: Showable) = new JpSuccess(res) - } - class JpError(msg: String) extends JpResult { - type ResultType = String - def isError = true - def value = msg - def show() = println(msg) // makes sense for :javap, less for -Ygen-javap - } - class JpSuccess(val value: Showable) extends JpResult { - type ResultType = AnyRef - def isError = false - def show() = value.show() // output to tool's PrintWriter - } - implicit class Lastly[A](val t: Try[A]) extends AnyVal { - private def effect[X](last: =>Unit)(a: X): Try[A] = { last; t } - def lastly(last: =>Unit): Try[A] = t transform (effect(last) _, effect(last) _) - } -} |