summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-03-29 02:59:31 +0000
committerPaul Phillips <paulp@improving.org>2011-03-29 02:59:31 +0000
commit5ebbba7a711105d8b6b19ce6497b3dcef0039c6f (patch)
tree1e625d215c3c5f4bb7ca0bb375f448cbdff1f880 /src
parentebedbef6d186c4cc98c9c4445967af43225230dc (diff)
downloadscala-5ebbba7a711105d8b6b19ce6497b3dcef0039c6f.tar.gz
scala-5ebbba7a711105d8b6b19ce6497b3dcef0039c6f.tar.bz2
scala-5ebbba7a711105d8b6b19ce6497b3dcef0039c6f.zip
Polishing the programmatic interface to the rep...
Polishing the programmatic interface to the repl and other bits of machinery which we'll have to live with for a while. The repl classloader now works more like you'd expect a classloader to, as seen here: % scala -Dscala.repl.power scala> class Bippus extends Traversable[Int] { def foreach[U](f: Int => U) = () } defined class Bippus scala> intp.classLoader.getResourceAsStream("Bippus").bytes() res0: Array[Byte] = Array(-54, -2, -70, ... scala> res0.size res1: Int = 23954 scala> case class Bippy(x: Int) defined class Bippy // classBytes is shorter way to say the same thing scala> intp.classLoader.classBytes("Bippy").size res2: Int = 2356 scala> intp.classLoader.classBytes("Bippy$").size res3: Int = 1741 Closes #4399, no review.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala41
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala4
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala37
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Phased.scala28
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Power.scala80
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/package.scala2
-rw-r--r--src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala21
-rw-r--r--src/compiler/scala/tools/util/Javap.scala2
-rw-r--r--src/scalap/scala/tools/scalap/Decode.scala4
9 files changed, 151 insertions, 68 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
index 39c27cfbb1..85dcff2086 100644
--- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala
@@ -5,8 +5,9 @@
package scala.tools.nsc
package interpreter
-import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.io.{ File, AbstractFile }
import util.ScalaClassLoader
+import java.net.URL
/**
* A class loader that loads files from a {@link scala.tools.nsc.io.AbstractFile}.
@@ -17,28 +18,42 @@ class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader)
extends ClassLoader(parent)
with ScalaClassLoader
{
- private def findBytes(name: String, onError: => Array[Byte]): Array[Byte] = {
+ protected def classNameToPath(name: String): String =
+ if (name endsWith ".class") name
+ else name.replace('.', '/') + ".class"
+
+ protected def findAbstractFile(name: String): AbstractFile = {
var file: AbstractFile = root
- val pathParts = name.split("[./]").toList
+ val pathParts = classNameToPath(name) split '/'
for (dirPart <- pathParts.init) {
file = file.lookupName(dirPart, true)
if (file == null)
- return onError
+ return null
}
- file.lookupName(pathParts.last+".class", false) match {
- case null => onError
- case file => file.toByteArray
+ file.lookupName(pathParts.last, false) match {
+ case null => null
+ case file => file
}
}
- override def findBytesForClassName(name: String): Array[Byte] =
- findBytes(name, super.findBytesForClassName(name))
-
+ override def getResourceAsStream(name: String) = findAbstractFile(name) match {
+ case null => super.getResourceAsStream(name)
+ case file => file.input
+ }
+ override def classBytes(name: String): Array[Byte] = findAbstractFile(name) match {
+ case null => super.classBytes(name)
+ case file => file.toByteArray
+ }
override def findClass(name: String): JClass = {
- val bytes = findBytes(name, throw new ClassNotFoundException(name))
- defineClass(name, bytes, 0, bytes.length)
+ val bytes = classBytes(name)
+ if (bytes.isEmpty) throw new ClassNotFoundException(name)
+ else defineClass(name, bytes, 0, bytes.length)
}
+ // Don't know how to construct an URL for something which exists only in memory
+ // override def getResource(name: String): URL = findAbstractFile(name) match {
+ // case null => super.getResource(name)
+ // case file => new URL(...)
+ // }
}
-
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index d018a5420d..8972bb6420 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -311,8 +311,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: PrintWriter)
// Look for Foo first, then Foo$, but if Foo$ is given explicitly,
// we have to drop the $ to find object Foo, then tack it back onto
// the end of the flattened name.
- def className = intp pathToFlatName path
- def moduleName = (intp pathToFlatName path.stripSuffix("$")) + "$"
+ def className = intp flatName path
+ def moduleName = (intp flatName path.stripSuffix("$")) + "$"
val bytes = super.tryClass(className)
if (bytes.nonEmpty) bytes
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 1a50b87569..5681196ef1 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -11,7 +11,7 @@ import java.io.{ PrintWriter }
import java.lang.reflect
import java.net.URL
import util.{ Set => _, _ }
-import io.VirtualDirectory
+import io.{ AbstractFile, VirtualDirectory }
import reporters.{ ConsoleReporter, Reporter }
import symtab.{ Flags, Names }
import scala.tools.nsc.interpreter.{ Results => IR }
@@ -78,7 +78,16 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
import formatting._
/** directory to save .class files to */
- val virtualDirectory = new VirtualDirectory("(memory)", None)
+ val virtualDirectory = new VirtualDirectory("(memory)", None) {
+ private def pp(root: io.AbstractFile, indentLevel: Int) {
+ val spaces = " " * indentLevel
+ out.println(spaces + root.name)
+ if (root.isDirectory)
+ root.toList sortBy (_.name) foreach (x => pp(x, indentLevel + 1))
+ }
+ // print the contents hierarchically
+ def show() = pp(this, 0)
+ }
/** reporter */
lazy val reporter: ConsoleReporter = new IMain.ReplReporter(this)
@@ -265,7 +274,19 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
if (parentClassLoader == null) ScalaClassLoader fromURLs compilerClasspath
else new URLClassLoader(compilerClasspath, parentClassLoader)
- new AbstractFileClassLoader(virtualDirectory, parent)
+ new AbstractFileClassLoader(virtualDirectory, 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 {
+ // deadlocks on startup if we try to translate names too early
+ case null if isInitializeComplete => generatedName(name) map (x => super.findAbstractFile(x)) orNull
+ case file => file
+ }
+ }
+ }
}
private def loadByName(s: String): Class[_] =
(classLoader tryToInitializeClass s) getOrElse sys.error("Failed to load expected class: '" + s + "'")
@@ -283,12 +304,12 @@ class IMain(val settings: Settings, protected val out: PrintWriter) {
*
* $line19.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$Bippy
*/
- def pathToFlatName(id: String): String = {
- requestForIdent(id) match {
- case Some(req) => req fullFlatName id
- case _ => id
- }
+ def generatedName(simpleName: String): Option[String] = {
+ if (simpleName endsWith "$") optFlatName(simpleName.init) map (_ + "$")
+ else optFlatName(simpleName)
}
+ def flatName(id: String) = optFlatName(id) getOrElse id
+ def optFlatName(id: String) = requestForIdent(id) map (_ fullFlatName id)
def allDefinedNames = definedNameMap.keys.toList sortBy (_.toString)
def pathToType(id: String): String = pathToName(newTypeName(id))
diff --git a/src/compiler/scala/tools/nsc/interpreter/Phased.scala b/src/compiler/scala/tools/nsc/interpreter/Phased.scala
index 1406d2af39..b3d33325e4 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Phased.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Phased.scala
@@ -6,6 +6,9 @@
package scala.tools.nsc
package interpreter
+import scala.collection.{ mutable, immutable }
+import immutable.SortedMap
+
/** Mix this into an object and use it as a phasing
* swiss army knife.
*/
@@ -63,11 +66,14 @@ trait Phased {
try parseInternal(str)
catch { case _: Exception => NoPhaseName }
- def apply[T](body: => T): T = atPhase(get)(body)
+ def apply[T](body: => T): SortedMap[PhaseName, T] =
+ SortedMap[PhaseName, T](atMap(PhaseName.all)(body): _*)
+
+ def atCurrent[T](body: => T): T = atPhase(get)(body)
def multi[T](body: => T): Seq[T] = multi map (ph => at(ph)(body))
- def all[T](body: => T): Seq[T] = ats(PhaseName.all)(body)
- def allshow[T](body: => T): Seq[T] = {
- val pairs = atz(PhaseName.all)(body)
+ def all[T](body: => T): Seq[T] = atMulti(PhaseName.all)(body)
+ def show[T](body: => T): Seq[T] = {
+ val pairs = atMap(PhaseName.all)(body)
pairs foreach { case (ph, op) => Console.println("%15s -> %s".format(ph, op.toString take 240)) }
pairs map (_._2)
}
@@ -75,25 +81,27 @@ trait Phased {
def at[T](ph: PhaseName)(body: => T): T = {
val saved = get
set(ph)
- try apply(body)
+ try atCurrent(body)
finally set(saved)
}
- def ats[T](phs: Seq[PhaseName])(body: => T): Seq[T] = {
+ def atMulti[T](phs: Seq[PhaseName])(body: => T): Seq[T] = {
val saved = multi
setMulti(phs)
try multi(body)
finally setMulti(saved)
}
- def atshow[T](phs: Seq[PhaseName])(body: => T): Unit =
- atz[T](phs)(body) foreach {
+ def showAt[T](phs: Seq[PhaseName])(body: => T): Unit =
+ atMap[T](phs)(body) foreach {
case (ph, op) => Console.println("%15s -> %s".format(ph, op.toString take 240))
}
- def atz[T](phs: Seq[PhaseName])(body: => T): Seq[(PhaseName, T)] =
- phs zip ats(phs)(body)
+ 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,
diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala
index 01b2d2328f..d608cccd56 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Power.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala
@@ -11,6 +11,9 @@ import scala.collection.{ mutable, immutable }
import scala.util.matching.Regex
import scala.tools.nsc.util.{ BatchSourceFile }
import session.{ History }
+import scala.io.Codec
+import java.net.{ URL, MalformedURLException }
+import io.{ Path }
trait SharesGlobal[G <: Global] {
val global: G
@@ -130,13 +133,14 @@ abstract class Power[G <: Global](
def init = """
|import scala.tools.nsc._
|import interpreter.Power
+ |import scala.collection.JavaConverters._
|final val global = repl.power.global
|final val power = repl.power.asInstanceOf[Power[global.type]]
|final val intp = repl.intp
|import global._
|import definitions._
- |import power.phased
|import power.Implicits.{ global => _, _ }
+ |import power.Utilities._
""".stripMargin
/** Starts up power mode and runs whatever is in init.
@@ -212,10 +216,10 @@ abstract class Power[G <: Global](
trait LowPriorityPrettifier {
implicit object AnyPrettifier extends Prettifier[Any] {
def prettify(x: Any): List[String] = x match {
- case x: Name => List(x.decode)
- case Tuple2(k, v) => List(prettify(k) ++ Seq("->") ++ prettify(v) mkString " ")
- case xs: Traversable[_] => (xs.toList flatMap prettify).sorted.distinct
- case x => List("" + x)
+ case x: Name => List(x.decode)
+ case Tuple2(k, v) => List(prettify(k) ++ Seq("->") ++ prettify(v) mkString " ")
+ case xs: TraversableOnce[_] => (xs.toList flatMap prettify).sorted.distinct
+ case x => List(Utilities.stringOf(x))
}
}
}
@@ -240,7 +244,7 @@ abstract class Power[G <: Global](
def grep(x: T, p: String => Boolean): Unit =
prettify(x) filter p foreach (x => println(spaces + x))
}
- class MultiPrintingConvenience[T: Prettifier](coll: Traversable[T]) {
+ class MultiPrintingConvenience[T: Prettifier](coll: TraversableOnce[T]) {
val pretty = implicitly[Prettifier[T]]
import pretty._
@@ -278,36 +282,72 @@ abstract class Power[G <: Global](
def >(r: Regex): Unit = >(_ matches r.pattern.toString)
def >(p: String => Boolean): Unit = pretty.grep(value, p)
}
+ class RichInputStream(in: InputStream)(implicit codec: Codec) {
+ def bytes(): Array[Byte] = io.Streamable.bytes(in)
+ def slurp(): String = io.Streamable.slurp(in)
+ }
+
protected trait Implicits1 {
// fallback
implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = new PrintingConvenience[T](x)
}
- object Implicits extends Implicits1 with SharesGlobal[G] {
- val global = Power.this.global
+ trait Implicits2 extends Implicits1 with SharesGlobal[G] {
import global._
- implicit lazy val powerNameOrdering: Ordering[Name] = Ordering[String] on (_.toString)
- implicit lazy val powerSymbolOrdering: Ordering[Symbol] = Ordering[Name] on (_.name)
- implicit lazy val powerTypeOrdering: Ordering[Type] = Ordering[Symbol] on (_.typeSymbol)
-
+ 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 def replCollPrinting[T: Prettifier](xs: Traversable[T]): MultiPrintingConvenience[T] = new MultiPrintingConvenience[T](xs)
+ implicit lazy val powerNameOrdering: Ordering[Name] = Ordering[String] on (_.toString)
+ implicit lazy val powerSymbolOrdering: Ordering[Symbol] = Ordering[Name] on (_.name)
+ implicit lazy val powerTypeOrdering: Ordering[Type] = Ordering[Symbol] on (_.typeSymbol)
+
+ implicit def replCollPrinting[T: Prettifier](xs: TraversableOnce[T]): MultiPrintingConvenience[T] = new MultiPrintingConvenience[T](xs)
implicit def replInternalInfo[T: Manifest](x: T): InternalInfo[T] = new InternalInfo[T](Some(x))
implicit def replPrettifier[T] : Prettifier[T] = Prettifier.default[T]
- implicit def vararsTypeApplication(sym: Symbol) = new {
- def apply(targs: Type*) = typeRef(NoPrefix, sym, targs.toList)
- }
+ implicit def replTypeApplication(sym: Symbol): RichSymbol = new RichSymbol(sym)
+ implicit def replInputStream(in: InputStream)(implicit codec: Codec): RichInputStream = new RichInputStream(in)
+ implicit def replInputStreamURL(url: URL)(implicit codec: Codec) = replInputStream(url.openStream())
+ }
+ object Implicits extends Implicits2 {
+ val global = Power.this.global
+ }
+ trait ReplUtilities {
def ?[T: Manifest] = InternalInfo[T]
+ def url(s: String) = {
+ try new URL(s)
+ catch { case _: MalformedURLException =>
+ if (Path(s).exists) Path(s).toURL
+ else new URL("http://" + s)
+ }
+ }
+ 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)
+ }
+ }
+ def stringOf(x: Any): String = scala.runtime.ScalaRunTime.stringOf(x)
}
-
- object phased extends Phased with SharesGlobal[G] {
- val global: G = Power.this.global
+ object Utilities extends ReplUtilities {
+ object phased extends Phased with SharesGlobal[G] {
+ val global: G = Power.this.global
+ }
}
+ lazy val phased = Utilities.phased
+
def context(code: String) = analyzer.rootContext(unit(code))
def source(code: String) = new BatchSourceFile("<console>", code)
def unit(code: String) = new CompilationUnit(source(code))
@@ -320,7 +360,7 @@ abstract class Power[G <: Global](
|Names: %s
|Identifiers: %s
""".stripMargin.format(
- phased.get,
+ Utilities.phased.get,
intp.allDefinedNames mkString " ",
intp.unqualifiedIds mkString " "
)
diff --git a/src/compiler/scala/tools/nsc/interpreter/package.scala b/src/compiler/scala/tools/nsc/interpreter/package.scala
index 13fb860128..b5ba2d9b8e 100644
--- a/src/compiler/scala/tools/nsc/interpreter/package.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/package.scala
@@ -26,6 +26,8 @@ package object interpreter {
type JClass = java.lang.Class[_]
type JList[T] = java.util.List[T]
type JCollection[T] = java.util.Collection[T]
+ type InputStream = java.io.InputStream
+ type OutputStream = java.io.OutputStream
private[nsc] val DebugProperty = "scala.repl.debug"
private[nsc] val TraceProperty = "scala.repl.trace"
diff --git a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
index be69c39547..d6d19eef92 100644
--- a/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
+++ b/src/compiler/scala/tools/nsc/util/ScalaClassLoader.scala
@@ -37,12 +37,8 @@ trait ScalaClassLoader extends JavaClassLoader {
Class.forName(path, initialize, this).asInstanceOf[Class[T]]
/** Create an instance of a class with this classloader */
- def create(path: String): AnyRef = {
- tryToInitializeClass(path) match {
- case Some(clazz) => clazz.newInstance()
- case None => null
- }
- }
+ def create(path: String): AnyRef =
+ tryToInitializeClass[AnyRef](path) map (_.newInstance()) orNull
override def findClass(name: String) = {
val result = super.findClass(name)
@@ -60,14 +56,15 @@ trait ScalaClassLoader extends JavaClassLoader {
manifest[T].erasure.getConstructors.toList map (_.asInstanceOf[Constructor[T]])
/** The actual bytes for a class file, or an empty array if it can't be found. */
- def findBytesForClassName(s: String): Array[Byte] = {
- val name = s.replaceAll("""\.""", "/") + ".class"
- val url = this.getResource(name)
-
- if (url == null) Array()
- else io.Streamable.bytes(url.openStream)
+ def classBytes(className: String): Array[Byte] = classAsStream(className) match {
+ case null => Array()
+ case stream => io.Streamable.bytes(stream)
}
+ /** An InputStream representing the given class name, or null if not found. */
+ def classAsStream(className: String) =
+ getResourceAsStream(className.replaceAll("""\.""", "/") + ".class")
+
/** Run the main method of a class to be loaded by this classloader */
def run(objectName: String, arguments: Seq[String]) {
val clsToRun = tryToInitializeClass(objectName) getOrElse (
diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala
index bda8ffbd1b..581cc9dbef 100644
--- a/src/compiler/scala/tools/util/Javap.scala
+++ b/src/compiler/scala/tools/util/Javap.scala
@@ -77,7 +77,7 @@ class Javap(
if (path endsWith ".class") (path dropRight 6).replace('/', '.')
else path
)
- loader.findBytesForClassName(extName)
+ loader.classBytes(extName)
}
}
diff --git a/src/scalap/scala/tools/scalap/Decode.scala b/src/scalap/scala/tools/scalap/Decode.scala
index c8bb58c81e..816041720f 100644
--- a/src/scalap/scala/tools/scalap/Decode.scala
+++ b/src/scalap/scala/tools/scalap/Decode.scala
@@ -33,7 +33,7 @@ object Decode {
*/
def scalaSigBytes(name: String): Option[Array[Byte]] = scalaSigBytes(name, getSystemLoader())
def scalaSigBytes(name: String, classLoader: ScalaClassLoader): Option[Array[Byte]] = {
- val bytes = classLoader.findBytesForClassName(name)
+ val bytes = classLoader.classBytes(name)
val reader = new ByteArrayReader(bytes)
val cf = new Classfile(reader)
cf.scalaSigAttribute map (_.data)
@@ -43,7 +43,7 @@ object Decode {
*/
def scalaSigAnnotationBytes(name: String): Option[Array[Byte]] = scalaSigAnnotationBytes(name, getSystemLoader())
def scalaSigAnnotationBytes(name: String, classLoader: ScalaClassLoader): Option[Array[Byte]] = {
- val bytes = classLoader.findBytesForClassName(name)
+ val bytes = classLoader.classBytes(name)
val byteCode = ByteCode(bytes)
val classFile = ClassFileParser.parse(byteCode)
import classFile._