diff options
-rw-r--r-- | src/compiler/scala/reflect/internal/Definitions.scala | 24 | ||||
-rw-r--r-- | src/compiler/scala/reflect/internal/Types.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/ILoop.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/Power.scala | 111 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/interpreter/ReplVals.scala | 75 | ||||
-rw-r--r-- | src/compiler/scala/tools/reflect/Mock.scala | 3 | ||||
-rw-r--r-- | test/files/run/repl-power.check | 22 | ||||
-rw-r--r-- | test/files/run/repl-power.scala | 4 |
8 files changed, 149 insertions, 98 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 15f89e1382..fe20613c22 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -386,6 +386,30 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val NoneModule: Symbol = getModule("scala.None") lazy val SomeModule: Symbol = getModule("scala.Some") + /** Note: don't use this manifest/type function for anything important, + * as it is incomplete. Would love to have things like existential types + * working, but very unfortunately the manifests just stuff the relevant + * information into the toString method. + */ + def manifestToType(m: OptManifest[_]): Type = m match { + case x: AnyValManifest[_] => + getClassIfDefined("scala." + x).tpe + case m: ClassManifest[_] => + val name = m.erasure.getName + if (name endsWith nme.MODULE_SUFFIX_STRING) + getModuleIfDefined(name stripSuffix nme.MODULE_SUFFIX_STRING).tpe + else { + val sym = getClassIfDefined(name) + val args = m.typeArguments + + if (sym eq NoSymbol) NoType + else if (args.isEmpty) sym.tpe + else appliedType(sym.typeConstructor, args map manifestToType) + } + case _ => + NoType + } + // The given symbol represents either String.+ or StringAdd.+ def isStringAddition(sym: Symbol) = sym == String_+ || sym == StringAdd_+ def isArrowAssoc(sym: Symbol) = ArrowAssocClass.tpe.decls.toList contains sym diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 47184eee51..38f51b1459 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -5487,6 +5487,10 @@ A type's typeSymbol should never be inspected directly. case _ => t } + def elimRefinement(t: Type) = t match { + case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) + case _ => t + } /** A collector that tests for existential types appearing at given variance in a type */ class ContainsVariantExistentialCollector(v: Int) extends TypeCollector(false) { diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 391d5ab8ee..3ddbffa75e 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -51,7 +51,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) intp.reporter.printMessage(msg) def isAsync = !settings.Yreplsync.value - lazy val power = Power(this) + lazy val power = new Power(intp, new StdReplVals(this)) // TODO // object opt extends AestheticSettings @@ -253,6 +253,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) /** Power user commands */ lazy val powerCommands: List[LoopCommand] = List( nullary("dump", "displays a view of the interpreter's internal state", dumpCommand), + nullary("vals", "gives information about the power mode repl vals", valsCommand), cmd("phase", "<phase>", "set the implicit phase for power commands", phaseCommand), cmd("wrap", "<method>", "name of method to wrap around each repl line", wrapCommand) withLongHelp (""" |:wrap @@ -283,6 +284,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) history.asStrings takeRight 30 foreach echo in.redrawLine() } + private def valsCommand(): Result = power.valsDescription private val typeTransforms = List( "scala.collection.immutable." -> "immutable.", diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala index ac7c2b1ecc..82a466a7e5 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Power.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala @@ -15,54 +15,12 @@ import scala.io.Codec import java.net.{ URL, MalformedURLException } import io.{ Path } -trait SharesGlobal { - type GlobalType <: Global - val global: GlobalType - - // This business gets really old: - // - // found : power.intp.global.Symbol - // required: global.Symbol - // - // Have tried many ways to cast it aside, this is the current winner. - // Todo: figure out a way to abstract over all the type members. - type AnySymbol = Global#Symbol - type AnyType = Global#Type - type AnyName = Global#Name - type AnyTree = Global#Tree - - type Symbol = global.Symbol - type Type = global.Type - type Name = global.Name - type Tree = global.Tree - - implicit def upDependentSymbol(x: AnySymbol): Symbol = x.asInstanceOf[Symbol] - implicit def upDependentType(x: AnyType): Type = x.asInstanceOf[Type] - implicit def upDependentName(x: AnyName): Name = x.asInstanceOf[Name] - implicit def upDependentTree(x: AnyTree): Tree = x.asInstanceOf[Tree] -} - -object Power { - def apply(intp: IMain): Power = apply(null, intp) - def apply(repl: ILoop): Power = apply(repl, repl.intp) - def apply(repl: ILoop, intp: IMain): Power = - new Power(repl, intp) { - type GlobalType = intp.global.type - final val global: intp.global.type = intp.global - } -} - /** A class for methods to be injected into the intp in power mode. */ -abstract class Power( - val repl: ILoop, - val intp: IMain -) extends SharesGlobal { - import intp.{ - beQuietDuring, typeOfExpression, interpret, parse - } - import global._ - import definitions.{ getClassIfDefined, getModuleIfDefined } +class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: ReplValsImpl) { + import intp.{ beQuietDuring, typeOfExpression, interpret, parse } + import intp.global._ + import definitions.{ manifestToType, getClassIfDefined, getModuleIfDefined } abstract class SymSlurper { def isKeep(sym: Symbol): Boolean @@ -130,11 +88,11 @@ abstract class Power( private def customInit = replProps.powerInitCode.option flatMap (f => io.File(f).safeSlurp()) def banner = customBanner getOrElse """ - |** Power User mode enabled - BEEP BOOP SPIZ ** + |** Power User mode enabled - BEEP WHIR GYVE ** |** :phase has been set to 'typer'. ** |** scala.tools.nsc._ has been imported ** - |** global._ and definitions._ also imported ** - |** Try :help, vals.<tab>, power.<tab> ** + |** global._, definitions._ also imported ** + |** Try :help, :vals, power.<tab> ** """.stripMargin.trim private def initImports = List( @@ -142,8 +100,9 @@ abstract class Power( "scala.collection.JavaConverters._", "intp.global.{ error => _, _ }", "definitions.{ getClass => _, _ }", - "power.Implicits._", - "power.rutil._" + "power.rutil._", + "replImplicits._", + "treedsl.CODE._" ) def init = customInit match { @@ -155,12 +114,23 @@ abstract class Power( */ def unleash(): Unit = beQuietDuring { // First we create the ReplVals instance and bind it to $r - intp.bind("$r", new ReplVals(repl)) + intp.bind("$r", replVals) // Then we import everything from $r. intp interpret ("import " + intp.pathToTerm("$r") + "._") // And whatever else there is to do. init.lines foreach (intp interpret _) } + def valsDescription: String = { + def to_str(m: Symbol) = "%12s %s".format( + m.decodedName, "" + elimRefinement(m.accessedOrSelf.tpe) stripPrefix "scala.tools.nsc.") + + ( rutil.info[ReplValsImpl].declares + filter (m => m.isPublic && !m.hasModuleFlag && !m.isConstructor) + sortBy (_.decodedName) + map to_str + mkString ("Name and type of values imported into the repl in power mode.\n\n", "\n", "") + ) + } trait LowPriorityInternalInfo { implicit def apply[T: Manifest] : InternalInfo[T] = new InternalInfo[T](None) @@ -180,25 +150,6 @@ abstract class Power( private def symbol = symbol_ private def name = name_ - // Would love to have stuff like existential types working, - // but very unfortunately those manifests just stuff the relevant - // information into the toString method. Boo. - private def manifestToType(m: Manifest[_]): Type = m match { - case x: AnyValManifest[_] => - getClassIfDefined("scala." + x).tpe - case _ => - val name = m.erasure.getName - if (name endsWith nme.MODULE_SUFFIX_STRING) - getModuleIfDefined(name stripSuffix nme.MODULE_SUFFIX_STRING).tpe - else { - val sym = getClassIfDefined(name) - val args = m.typeArguments - - if (args.isEmpty) sym.tpe - else typeRef(NoPrefix, sym, args map manifestToType) - } - } - def symbol_ : Symbol = getClassIfDefined(erasure.getName) def tpe_ : Type = manifestToType(man) def name_ : Name = symbol.name @@ -208,9 +159,10 @@ abstract class Power( def owner = symbol.owner def owners = symbol.ownerChain drop 1 def defn = symbol.defString + def decls = symbol.info.decls - def declares = members filter (_.owner == symbol) - def inherits = members filterNot (_.owner == symbol) + def declares = decls.toList + def inherits = members filterNot (declares contains _) def types = members filter (_.name.isTypeName) def methods = members filter (_.isMethod) def overrides = declares filter (_.isOverride) @@ -233,8 +185,8 @@ abstract class Power( def whoHas(name: String) = bts filter (_.decls exists (_.name.toString == name)) def <:<[U: Manifest](other: U) = tpe <:< InternalInfo[U].tpe - def lub[U: Manifest](other: U) = global.lub(List(tpe, InternalInfo[U].tpe)) - def glb[U: Manifest](other: U) = global.glb(List(tpe, InternalInfo[U].tpe)) + def lub[U: Manifest](other: U) = intp.global.lub(List(tpe, InternalInfo[U].tpe)) + def glb[U: Manifest](other: U) = intp.global.glb(List(tpe, InternalInfo[U].tpe)) def shortClass = erasure.getName split "[$.]" last override def toString = value match { @@ -337,7 +289,7 @@ abstract class Power( def pp() { intp prettyPrint slurp() } } - protected trait Implicits1 { + trait Implicits1 { // fallback implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) = new SinglePrettifierClass[T](x) @@ -367,7 +319,6 @@ abstract class Power( implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in) implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec) } - object Implicits extends Implicits2 { } trait ReplUtilities { def module[T: Manifest] = getModuleIfDefined(manifest[T].erasure.getName stripSuffix nme.MODULE_SUFFIX_STRING) @@ -396,11 +347,7 @@ abstract class Power( } lazy val rutil: ReplUtilities = new ReplUtilities { } - - lazy val phased: Phased = new Phased with SharesGlobal { - type GlobalType = Power.this.global.type - final val global: Power.this.global.type = Power.this.global - } + lazy val phased: Phased = new { val global: intp.global.type = intp.global } with Phased { } def context(code: String) = analyzer.rootContext(unit(code)) def source(code: String) = new BatchSourceFile("<console>", code) diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala index 2f2489b242..6e5dec4205 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala @@ -6,15 +6,68 @@ package scala.tools.nsc package interpreter -final class ReplVals(r: ILoop) { - lazy val repl = r - lazy val intp = r.intp - lazy val power = r.power - lazy val reader = r.in - lazy val vals = this - lazy val global = intp.global - lazy val isettings = intp.isettings - lazy val completion = reader.completion - lazy val history = reader.history - lazy val phased = power.phased +/** 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 + + final lazy val treedsl = new { val global: intp.global.type = intp.global } with ast.TreeDSL { } + final lazy val typer = analyzer.newTyper( + analyzer.rootContext( + power.unit("").asInstanceOf[analyzer.global.CompilationUnit] + ) + ) + + final lazy val replImplicits = new power.Implicits2 { + import intp.global._ + + private val manifestFn = ReplVals.mkManifestToType[intp.global.type](global) + implicit def mkManifestToType(sym: Symbol) = manifestFn(sym) + } + + 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 mkManifestToType[T <: Global](global: T) = { + import global._ + import definitions._ + + /** We can't use definitions.manifestToType 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 manifestToType(m: OptManifest[_]): Global#Type = + definitions.manifestToType(m) + + class AppliedTypeFromManifests(sym: Symbol) { + def apply[M](implicit m1: Manifest[M]): Type = + appliedType(sym.typeConstructor, List(m1) map (x => manifestToType(x).asInstanceOf[Type])) + + def apply[M1, M2](implicit m1: Manifest[M1], m2: Manifest[M2]): Type = + appliedType(sym.typeConstructor, List(m1, m2) map (x => manifestToType(x).asInstanceOf[Type])) + } + + (sym: Symbol) => new AppliedTypeFromManifests(sym) + } } diff --git a/src/compiler/scala/tools/reflect/Mock.scala b/src/compiler/scala/tools/reflect/Mock.scala index 5301816b4b..52c052b8a2 100644 --- a/src/compiler/scala/tools/reflect/Mock.scala +++ b/src/compiler/scala/tools/reflect/Mock.scala @@ -25,7 +25,8 @@ trait Mock extends (Invoked => AnyRef) { def newInvocationHandler() = new InvocationHandler { def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]) = - mock(Invoked(proxy, method, args)) + try { mock(Invoked(proxy, method, args)) } + catch { case _: NoClassDefFoundError => sys.exit(1) } } } diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check index 38e7532133..1e7b6f0cd8 100644 --- a/test/files/run/repl-power.check +++ b/test/files/run/repl-power.check @@ -2,15 +2,31 @@ Type in expressions to have them evaluated. Type :help for more information. scala> :power -** Power User mode enabled - BEEP BOOP SPIZ ** +** Power User mode enabled - BEEP WHIR GYVE ** ** :phase has been set to 'typer'. ** ** scala.tools.nsc._ has been imported ** -** global._ and definitions._ also imported ** -** Try :help, vals.<tab>, power.<tab> ** +** global._, definitions._ also imported ** +** Try :help, :vals, power.<tab> ** scala> // guarding against "error: reference to global is ambiguous" scala> global.emptyValDef // "it is imported twice in the same scope by ..." res0: $r.global.emptyValDef.type = private val _ = _ +scala> val tp = ArrayClass[scala.util.Random] // magic with manifests +tp: $r.global.Type = Array[scala.util.Random] + +scala> tp.memberType(Array_apply) // evidence +res1: $r.global.Type = (i: Int)scala.util.Random + +scala> val m = LIT(10) MATCH (CASE(LIT(5)) ==> FALSE, DEFAULT ==> TRUE) // treedsl +m: $r.treedsl.global.Match = +10 match { + case 5 => false + case _ => true +} + +scala> typed(m).tpe // typed is in scope +res2: $r.treedsl.global.Type = Boolean + scala> diff --git a/test/files/run/repl-power.scala b/test/files/run/repl-power.scala index 9f70ac4b68..27da3df106 100644 --- a/test/files/run/repl-power.scala +++ b/test/files/run/repl-power.scala @@ -5,6 +5,10 @@ object Test extends ReplTest { :power // guarding against "error: reference to global is ambiguous" global.emptyValDef // "it is imported twice in the same scope by ..." +val tp = ArrayClass[scala.util.Random] // magic with manifests +tp.memberType(Array_apply) // evidence +val m = LIT(10) MATCH (CASE(LIT(5)) ==> FALSE, DEFAULT ==> TRUE) // treedsl +typed(m).tpe // typed is in scope """.trim } |