scala.tools.nsc.interpreter.Power.scala Maven / Gradle / Ivy
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.tools.nsc.interpreter
import java.io.InputStream
import java.net.{URI, URL}
import scala.collection.mutable
import scala.io.Codec
import scala.language.implicitConversions
import scala.reflect.ClassTag
import scala.reflect.runtime.{universe => ru}
import scala.tools.nsc.io
/** 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.global._
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: @unchecked)
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()
}
}
}
def classic = """
|** 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. **
""".stripMargin.trim
def banner = """
|Power mode enabled. :phase is at typer.
|import scala.tools.nsc._, intp.global._, definitions._
|Try :help or completions for vals._ and power._
""".stripMargin.trim
val initImports = List(
"import scala.tools.nsc._",
"import scala.jdk.CollectionConverters._",
"import intp.global.{ error => _, _ }",
"import definitions.{ getClass => _, _ }",
"import power.rutil._",
"import replImplicits._",
"import treedsl.CODE._")
/** Quietly starts up power mode and runs whatever is in init.
*/
def unleash(): Unit = intp.reporter.withoutPrintingResults {
// First we create the ReplVals instance and bind it to $r
intp.bind("$r", replVals)
// Then we import everything from $r.
intp interpret s"import ${ intp.originalPath("$r") }._"
}
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 accidentally 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"
/** Standard noise reduction filter. */
def excludeMember(s: Symbol) = (
isSpecialized(s)
|| s.isAnonOrRefinementClass
|| s.isAnonymousFunction
)
def symbol = definitions.compilerSymbolFromTag(tag)
def tpe = definitions.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).iterator foreach println
def prettify(x: Any): IterableOnce[String] = x match {
case x: Name => List(x.decode)
case Tuple2(k, v) => List(prettify(k).iterator ++ Iterator("->") ++ prettify(v) mkString " ")
case xs: Array[_] => xs.iterator flatMap prettify
case xs: IterableOnce[_] => xs.iterator 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): IterableOnce[String] = AnyPrettifier prettify x
def show(x: T): Unit = AnyPrettifier show x
}
}
trait Prettifier[T] {
def show(x: T): Unit
def prettify(x: T): IterableOnce[String]
def prettify(xs: IterableOnce[T]): IterableOnce[String] = xs.iterator 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)).iterator foreach (StringPrettifier show _)
def freq[U](p: T => U) = value.groupMapReduce(p)(_ => 1)(_ + _).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 URI(s).toURL
else if (new java.io.File(s).exists) new java.io.File(s).toURI.toURL
else new URI("http://" + s).toURL
)
}
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]): PrettifierClass[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: IterableOnce[T]): MultiPrettifierClass[T] =
new MultiPrettifierClass[T](Seq.from(xs))
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): RichInputStream = 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) = intp.parse(code).map(_._1).getOrElse(Nil)
override def toString = s"""
|** Power mode status **
|Default phase: ${phased.get}
|Names: ${intp.unqualifiedIds mkString " "}
""".stripMargin
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy