summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/interactive/Global.scala
blob: 325718f8651c724b7825acff149ea695ef7284da (plain) (tree)
1
2
3
4
5
6
7
8
9

                                   
                                                                





                                                                                                            

                                                                                            
                                                    




                                        

       
                                                                        
                                                                          
                                                   
     
                                       
 
                                                                                             
     

                                                                            
 


                                           

                                             



                                                            
                                                                

                          
                                                                     

                          

                                                                       
                               

                                                                               
 




                                                                                 



                                                                                   
                                                                                 


                                   
                                  
                 
                                      




                           


                                            






                                                                      



                                                                                   
                     

                                                            
                                                           
                                          













                                     



                                                                           

                                                 
 

                                                


                                                                           


                                     
                             
                 



                                     
                               


             






                                         

       
           

   
                             
     
                                   
                                                                   
                  



                                                                 
                                          

   








                                              

                             

     






                                                


                                                                        
               

                            





                                              
                                         




                                                                                  







                                                             
                                                                  

                                                                 



                                                  
                   
                                                             

                          
        
     

                                        

   








                                                           
                                                                                    

                                                             

   



























                                                                                                              
 

                                                                











                                                                               

                                             
                                          
               
                         



                                              

                      
                            
       








                                              
                                                                             
 
                                                                              





                                                        
                                                  













                                                


                                              












                                                                          

                             
 
 
package scala.tools.nsc.interactive

import scala.collection.mutable.{LinkedHashMap, SynchronizedMap}
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.{SourceFile, Position, RangePosition, OffsetPosition, NoPosition, WorkScheduler}
import scala.tools.nsc.reporters._
import scala.tools.nsc.symtab._
import scala.tools.nsc.ast._

/** The main class of the presentation compiler in an interactive environment such as an IDE
 */
class Global(settings: Settings, reporter: Reporter)
  extends nsc.Global(settings, reporter)
     with CompilerControl
     with Positions
     with ContextTrees
     with RichCompilationUnits {
self =>

  /** A list indicating in which order some units should be typechecked.
   *  All units in firsts are typechecked before any unit not in this list
   *  Modified by askToDoFirst, reload, typeAtTree.
   */
  var firsts: List[SourceFile] = List()

  /** A map of all loaded files units to the rich compilation units that corresponds to them.
   */
  val unitOfFile = new LinkedHashMap[AbstractFile, RichCompilationUnit] with
                       SynchronizedMap[AbstractFile, RichCompilationUnit]

  /** The currently active typer run */
  private var currentTyperRun: TyperRun = _

  /** Is a background compiler run needed? */
  private var outOfDate = false

  /** Is a reload/ background compiler currently running? */
  private var acting = false

  /** The status value of a unit that has not yet been loaded */
  final val NotLoaded = -1

  /** The status value of a unit that has not yet been typechecked */
  final val JustParsed = 0

  // ----------- Overriding hooks in nsc.Global -----------------------

  /** Create a RangePosition */
  override def rangePos(source: SourceFile, start: Int, point: Int, end: Int) =
    new RangePosition(source, start, point, end)

  /** Called from typechecker: signal that a node has been completely typechecked
   *  @param  context  The context that typechecked the node
   *  @param  old      The original node
   *  @param  result   The transformed node
   */
  override def signalDone(context: Context, old: Tree, result: Tree) {
    def integrateNew() {
      context.unit.body = new TreeReplacer(old, result) transform context.unit.body
    }
    if ((context.unit != null) && (context.unit.targetPos includes result.pos)) {
      integrateNew()
      throw new TyperResult(result)
    }
    val typerRun = currentTyperRun
    pollForWork()
    if (typerRun != currentTyperRun) {
      integrateNew()
      throw new FreshRunReq
    }
  }

  /** Called every time a context is created
   *  Register the context in a context tree
   */
  override def registerContext(c: Context) = c.unit match {
    case u: RichCompilationUnit => addContext(u.contexts, c)
    case _ =>
  }

  // ----------------- Polling ---------------------------------------

  /** Called from runner thread and signalDone:
   *  Poll for exeptions.
   *  Poll for work reload/typedTreeAt/doFirst commands during background checking.
   */
  def pollForWork() {
    scheduler.pollException() match {
      case Some(ex: CancelActionReq) => if (acting) throw ex
      case Some(ex: FreshRunReq) => if (outOfDate) throw ex
      case Some(ex: Throwable) => throw ex
      case _ =>
    }
    scheduler.nextWorkItem() match {
      case Some(action) =>
        try {
          acting = true
          action()
        } catch {
          case ex: CancelActionReq =>
        } finally {
          acting = false
        }
      case None =>
    }
  }

  // ----------------- The Background Runner Thread -----------------------

  /** The current presentation compiler runner */
  private var compileRunner = newRunnerThread

  /** Create a new presentation compiler runner.
   */
  def newRunnerThread: Thread = new Thread("Scala Presentation Compiler") {
    override def run() {
      try {
        while (true) {
          scheduler.waitForMoreWork()
          pollForWork()
          while (outOfDate) {
            try {
              backgroundCompile()
            } catch {
              case ex: FreshRunReq =>
            } finally {
              outOfDate = false
            }
          }
        }
      } catch {
        case ex: ShutdownReq =>
          ;
        case ex =>
          ex.printStackTrace()
          inform("Fatal Error: "+ex)
          compileRunner = newRunnerThread
      }
    }
    start()
  }

  /** Compile all given units
   */
  private def backgroundCompile() {
    inform("Starting new presentation compiler type checking pass")
    reporter.reset
    firsts = firsts filter (s => unitOfFile contains (s.file))
    val prefix = firsts map unitOf
    val units = prefix ::: (unitOfFile.values.toList diff prefix)
    units foreach recompile
    inform("Everything is now up to date")
  }

  /** Reset unit to just-parsed state */
  def reset(unit: RichCompilationUnit): Unit =
    if (unit.status > JustParsed) {
      unit.depends.clear()
      unit.defined.clear()
      unit.synthetics.clear()
      unit.toCheck.clear()
      unit.targetPos = NoPosition
      unit.contexts.clear()
      unit.body = EmptyTree
      unit.status = NotLoaded
    }

  /** Parse unit and create a name index. */
  def parse(unit: RichCompilationUnit): Unit = {
    currentTyperRun.compileLate(unit)
    validatePositions(unit.body)
    unit.status = JustParsed
  }

  /** Make sure symbol and type attributes are reset and recompile unit.
   */
  def recompile(unit: RichCompilationUnit) {
    reset(unit)
    inform("parsing: "+unit)
    parse(unit)
    inform("type checking: "+unit)
    currentTyperRun.typeCheck(unit)
    unit.status = currentRunId
  }

  /** Move list of files to front of firsts */
  def moveToFront(fs: List[SourceFile]) {
    firsts = fs ::: (firsts diff fs)
  }

  // ----------------- Implementations of client commmands -----------------------

  def respond[T](result: Response[T])(op: => T): Unit = try {
    result set Left(op)
  } catch {
    case ex =>
      result set Right(ex)
      throw ex
  }

  /** Make sure a set of compilation units is loaded and parsed */
  def reload(sources: List[SourceFile], result: Response[Unit]) {
    respond(result) {
      currentTyperRun = new TyperRun()
      for (source <- sources) {
        val unit = new RichCompilationUnit(source)
        unitOfFile(source.file) = unit
        parse(unit)
        if (settings.Xprintpos.value) treePrinter.print(unit)
      }
      moveToFront(sources)
      ()
    }
    if (outOfDate) throw new FreshRunReq
    else outOfDate = true
  }

  /** A fully attributed tree located at position `pos`  */
  def typedTreeAt(pos: Position): Tree = {
    val unit = unitOf(pos)
    assert(unit.status != NotLoaded)
    moveToFront(List(unit.source))
    val typedTree = currentTyperRun.typedTreeAt(pos)
    new Locator(pos) locateIn typedTree
  }

  /** Set sync var `result` to a fully attributed tree located at position `pos`  */
  def getTypedTreeAt(pos: Position, result: Response[Tree]) {
    respond(result)(typedTreeAt(pos))
  }

  def stabilizedType(tree: Tree): Type = tree match {
    case Ident(_) if tree.symbol.isStable => singleType(NoPrefix, tree.symbol)
    case Select(qual, _) if tree.symbol.isStable => singleType(qual.tpe, tree.symbol)
    case _ => tree.tpe
  }

  def completion(pos: Position, result: Response[List[Member]]) {
    import MemberStatus._
    respond(result) {
      val tree = typedTreeAt(pos)
      locateContext(pos) match {
        case Some(context) =>
          val superAccess = tree.isInstanceOf[Super]
          val pre = stabilizedType(tree)
          def withStatus(sym: Symbol, vs: ValueSet) = (
            sym,
            pre.memberType(sym),
            if (context.isAccessible(sym, pre, superAccess)) vs + Accessible else vs
          )
          val decls = tree.tpe.decls.toList map (sym => withStatus(sym, ValueSet()))
          val inherited = tree.tpe.members.toList diff decls map (sym => withStatus(sym, ValueSet(Inherited)))
          val implicits = List() // not yet done
          decls ::: inherited ::: implicits
        case None =>
          throw new FatalError("no context found for "+pos)
      }
    }
  }

  // ---------------- Helper classes ---------------------------

  /** A transformer that replaces tree `from` with tree `to` in a given tree */
  class TreeReplacer(from: Tree, to: Tree) extends Transformer {
    override def transform(t: Tree): Tree = {
      if (t.pos includes from.pos)
        if (t == from) to
        else super.transform(t)
      else
        t
    }
  }

  /** A traverser that resets all type and symbol attributes in a tree */
  object ResetAttrs extends Transformer {
    override def transform(t: Tree): Tree = {
      if (t.hasSymbol) t.symbol = NoSymbol
      t match {
        case EmptyTree =>
          t
        case tt: TypeTree =>
          if (tt.original != null) tt.original
          else t
        case _ =>
          t.tpe = null
          super.transform(t)
      }
    }
  }

  /** The typer run */
  class TyperRun extends Run {
    // units is always empty
    // symSource, symData are ignored
    override def compiles(sym: Symbol) = false

    def typeCheck(unit: CompilationUnit): Unit = applyPhase(typerPhase, unit)

    def enterNames(unit: CompilationUnit): Unit = applyPhase(namerPhase, unit)

    /** Return fully attributed tree at given position
     *  (i.e. largest tree that's contained by position)
     */
    def typedTreeAt(pos: Position): Tree = {
      val tree = locateTree(pos)
//      println("at pos "+pos+" was found: "+tree)
      if (tree.tpe ne null) tree
      else {
        val unit = unitOf(pos)
        assert(unit.status >= JustParsed)
        unit.targetPos = pos
        try {
          typeCheck(unit)
          throw new FatalError("tree not found")
        } catch {
          case ex: TyperResult => ex.tree
        }
      }
    }

    /** Apply a phase to a compilation unit
     *  @return true iff typechecked correctly
     */
    private def applyPhase(phase: Phase, unit: CompilationUnit) {
      val oldSource = reporter.getSource
      try {
        reporter.setSource(unit.source)
        atPhase(phase) { phase.asInstanceOf[GlobalPhase] applyPhase unit }
      } finally {
        reporter setSource oldSource
      }
    }
  }

  class TyperResult(val tree: Tree) extends Exception

  assert(globalPhase.id == 0)

}