summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/interpreter/Power.scala
blob: 8e546519e5d876ecf65c4c6cb98ca73c7cc29eaa (plain) (tree)
1
2
3
4
5
6
7
8
9







                                
                                    
                                              
                                
                                               
                          


                                              
 



























                                                                               
                                                                  

                          
                                                                         

 
                                                                   
   

                                  
                 
                           
                                                 
                                                                                                   
 


































                                                                                              
 




                                           

   









                                                                   
 




                                                                                                 

   



                                                                                                 
                                                   
                                                   

                                                   



                                                   
                                     
                             
                                             
                                    
                             



                                                        
                                       
                               
                                  









                                         
                                     

   



                                                         


                                                                                        
                                 
                                                                                 
   

                                                         



                                                                            




                                                            
                                            








                                                         
                                                                                                        
 





                                                                           

                                                                     
                                    



                                                         











                                                                                           

   


                                                           

                                                                                                 

                                                                          










                                                                       








                                           


                                                             
   
                                                                           

                                          








                                              

                                                               













                                                                  
   
 


                                                      
                            

                                                           
                                                             
   




                                                                 
                              

                                                                                                                              
   
                                                            

                   



                                                                          
                                                           




                                           




                                                                                                                                           
                                                                                                    
                                                                          




                                                                                                                   
                                     
   
 
                       
                                        



















                                                                                   
   



                                                             
   
 




                                                                           






                             
                 
                                        
                                      

     
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Paul Phillips
 */

package scala.tools.nsc
package interpreter

import scala.reflect.NameTransformer
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

  // 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[G <: Global](repl: ILoop, g: G) =
    new { final val global: G = g } with Power[G](repl, repl.intp)

  def apply(intp: IMain) =
    new { final val global = intp.global } with Power[Global](null, intp)
}

/** A class for methods to be injected into the intp in power mode.
 */
abstract class Power[G <: Global](
  val repl: ILoop,
  val intp: IMain
) extends SharesGlobal[G] {
  import intp.{ beQuietDuring, interpret, parse }
  import global.{ opt, definitions, stringToTermName, NoSymbol, NoType, analyzer, CompilationUnit }

  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
      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 (opt.verbose) {
        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(pkgName: String) extends SymSlurper {
    val pkgSymbol = getCompilerModule(pkgName)
    val modClass  = pkgSymbol.moduleClass

    /** Looking for dwindling returns */
    def droppedEnough() = unseenHistory.size >= 4 && (
      unseenHistory.takeRight(4).sliding(2) map (_.toList) forall {
        case List(a, b) => a > b
      }
    )

    def isRecur(sym: Symbol)  = true
    def isIgnore(sym: Symbol) = sym.isAnonOrRefinementClass || (sym.name.toString contains "$mc")
    def isKeep(sym: Symbol)   = sym.hasTransOwner(modClass)
    def isFinished()          = droppedEnough()
    def slurp()               = apply(modClass)
  }

  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 BOOP WHIR **
    |** scala.tools.nsc._ has been imported      **
    |** global._ and definitions._ also imported **
    |** New vals! Try repl, intp, global, power  **
    |** New cmds! :help to discover them         **
    |** New defs! Type power.<tab> to reveal     **
  """.stripMargin.trim

  def init = customInit getOrElse """
    |import scala.tools.nsc._
    |import scala.collection.JavaConverters._
    |import global.{ error => _, _ }
    |import power.Implicits._
  """.stripMargin

  /** Starts up power mode and runs whatever is in init.
   */
  def unleash(): Unit = beQuietDuring {
    val r  = new ReplVals(repl)
    intp.bind[ILoop]("repl", repl)
    intp.bind[ReplVals]("$r", r)

    intp.bind("intp", r.intp)
    intp.bind("global", r.global)
    intp.bind("power", r.power)
    intp.bind("phased", r.phased)
    intp.bind("isettings", r.isettings)
    intp.bind("completion", r.completion)
    intp.bind("history", r.history)

    init split '\n' foreach interpret
  }

  private def missingWrap(op: => Symbol): Symbol =
    try op
    catch { case _: MissingRequirementError => NoSymbol }

  private def getCompilerClass(name: String)  = missingWrap(definitions.getClass(name))
  private def getCompilerModule(name: String) = missingWrap(definitions.getModule(name))

  trait LowPriorityInternalInfo {
    implicit def apply[T: Manifest] : InternalInfo[T] = new InternalInfo[T](None)
  }
  object InternalInfo extends LowPriorityInternalInfo { }

  /** Todos...
   *    translate manifest type arguments into applied types
   *    customizable symbol filter (had to hardcode no-spec to reduce noise)
   */
  class InternalInfo[T: Manifest](value: Option[T] = None) {
    def companion = symbol.companionSymbol
    def info      = symbol.info
    def module    = symbol.moduleClass
    def owner     = symbol.owner
    def owners    = symbol.ownerChain drop 1
    def symDef    = symbol.defString
    def symName   = symbol.name
    def tpe       = symbol.tpe

    def declares  = members filter (_.owner == symbol)
    def inherits  = members filterNot (_.owner == symbol)
    def types     = members filter (_.name.isTypeName)
    def methods   = members filter (_.isMethod)
    def overrides = declares filter (_.isOverride)
    def inPackage = owners find (x => x.isPackageClass || x.isPackage) getOrElse definitions.RootPackage

    def erasure    = manifest[T].erasure
    def symbol     = getCompilerClass(erasure.getName)
    def members    = tpe.members filterNot (_.name.toString contains "$mc")
    def allMembers = tpe.members
    def bts        = info.baseTypeSeq.toList
    def btsmap     = bts map (x => (x, x.decls.toList)) toMap
    def pkgName    = Option(erasure.getPackage) map (_.getName)
    def pkg        = pkgName map getCompilerModule getOrElse NoSymbol
    def pkgmates   = pkg.tpe.members
    def pkgslurp   = pkgName match {
      case Some(name) => new PackageSlurper(name) slurp()
      case _          => Set()
    }
    def ?         = this

    def whoHas(name: String) = bts filter (_.decls.toList 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 shortClass = erasure.getName split "[$.]" last
    override def toString = value match {
      case Some(x)  => "%s (%s)".format(x, shortClass)
      case _        => erasure.getName
    }
  }

  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: TraversableOnce[_] => (xs.toList flatMap prettify).sorted
        case x                      => List(rutil.stringOf(x))
      }
    }
  }
  object Prettifier extends LowPriorityPrettifier {
    def prettify[T](value: T): List[String] = default[T] prettify value
    def default[T] = new Prettifier[T] {
      def prettify(x: T): List[String] = AnyPrettifier prettify x
    }
  }
  trait Prettifier[T] {
    def prettify(x: T): List[String]

    private var indentLevel = 0
    private def spaces = "  " * indentLevel
    def indented[T](body: => T): T = {
      indentLevel += 1
      try body
      finally indentLevel -= 1
    }

    def show(x: T): Unit = grep(x, _ => true)
    def grep(x: T, p: String => Boolean): Unit =
      prettify(x) filter p foreach (x => println(spaces + x))
  }
  class MultiPrintingConvenience[T: Prettifier](coll: TraversableOnce[T]) {
    val pretty = implicitly[Prettifier[T]]
    import pretty._

    def freqBy[U](p: T => U) = {
      val map = coll.toList groupBy p
      map.toList sortBy (-_._2.size)
    }
    def freqByFormatted[U](p: T => U) = {
      val buf = new mutable.ListBuffer[String]

      freqBy(p) foreach { case (k, vs) =>
        buf += "%d: %s".format(vs.size, Prettifier.prettify(k))
        vs flatMap prettify foreach (buf += "  " + _)
      }
      buf.toList
    }

    /** It makes sense.
     *
     *    #  means how many
     *    ?  means "I said, HOW MANY?"
     *    >  means print
     *
     *  Now don't you feel silly for what you were thinking.
     */
    def #?>[U](p: T => U) = this freqByFormatted p foreach println
    def #?[U](p: T => U) = this freqByFormatted p
  }

  class PrintingConvenience[T: Prettifier](value: T) {
    val pretty = implicitly[Prettifier[T]]

    def >() { >(_ => true) }
    def >(s: String): Unit = >(_ contains s)
    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)
  }
  trait Implicits2 extends Implicits1 with SharesGlobal[G] {
    import global._

    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 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 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: G = 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)
  }

  lazy val rutil: ReplUtilities = new ReplUtilities { }
  lazy val phased: Phased = new Phased with SharesGlobal[G] {
    val global: G = Power.this.global
  }

  def context(code: String)    = analyzer.rootContext(unit(code))
  def source(code: String)     = new BatchSourceFile("<console>", code)
  def unit(code: String)       = new CompilationUnit(source(code))
  def trees(code: String)      = parse(code) getOrElse Nil
  def typeOf(id: String): Type = intp.typeOfExpression(id) getOrElse NoType

  override def toString = """
    |** Power mode status **
    |Default phase: %s
    |Names: %s
    |Identifiers: %s
  """.stripMargin.format(
      phased.get,
      intp.allDefinedNames mkString " ",
      intp.unqualifiedIds mkString " "
    )
}