aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/Pickler.scala
blob: bf37067b1c2ee017c26e65474afe591fe03daf86 (plain) (tree)
1
2
3
4
5
6
7
8
9



                        

                       
              
                                            
                          
                
               

                   
                         
 
                               
                             


                                            
 




                                                   
 
                                                      
                                                                       
                                                                       






                                                                                                               
 

                                                   
                                                       
 

                                                                          
                                      



                                            
                                   
                                 
                          
                                                 
                                           
                          
                                                                                         
 



                                           
                                                                             
                                                              





                                                                 
       
     
   
 

                                                                                                    
                                        
                    


                                                           

          
 
                                                            
                                                              
                    
                    
                                              
                                                                   
                                      
                        
       
                                                                  
                                          
                                    
                                                           
     
   
 
                                                                                                      


                                             
                                                                                     
                    
                                                                     
     
 
package dotty.tools.dotc
package transform

import core._
import Contexts.Context
import Decorators._
import tasty._
import config.Printers.{noPrinter, pickling}
import java.io.PrintStream
import Periods._
import Phases._
import Symbols._
import Flags.Module
import collection.mutable

/** This phase pickles trees */
class Pickler extends Phase {
  import ast.tpd._

  override def phaseName: String = "pickler"

  private def output(name: String, msg: String) = {
    val s = new PrintStream(name)
    s.print(msg)
    s.close
  }

  // Maps that keep a record if -Ytest-pickler is set.
  private val beforePickling = new mutable.HashMap[ClassSymbol, String]
  private val picklers = new mutable.HashMap[ClassSymbol, TastyPickler]

  /** Drop any elements of this list that are linked module classes of other elements in the list */
  private def dropCompanionModuleClasses(clss: List[ClassSymbol])(implicit ctx: Context): List[ClassSymbol] = {
    val companionModuleClasses =
      clss.filterNot(_ is Module).map(_.linkedClass).filterNot(_.isAbsent)
    clss.filterNot(companionModuleClasses.contains)
  }

  override def run(implicit ctx: Context): Unit = {
    val unit = ctx.compilationUnit
    pickling.println(i"unpickling in run ${ctx.runId}")

    for { cls <- dropCompanionModuleClasses(topLevelClasses(unit.tpdTree))
          tree <- sliceTopLevel(unit.tpdTree, cls) } {
      val pickler = new TastyPickler()
      if (ctx.settings.YtestPickler.value) {
        beforePickling(cls) = tree.show
        picklers(cls) = pickler
      }
      val treePkl = pickler.treePkl
      treePkl.pickle(tree :: Nil)
      treePkl.compactify()
      pickler.addrOfTree = treePkl.buf.addrOfTree
      pickler.addrOfSym = treePkl.addrOfSym
      if (tree.pos.exists)
        new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil)

      // other pickle sections go here.
      val pickled = pickler.assembleParts()
      unit.pickled += (cls -> pickled)

      def rawBytes = // not needed right now, but useful to print raw format.
        pickled.iterator.grouped(10).toList.zipWithIndex.map {
          case (row, i) => s"${i}0: ${row.mkString(" ")}"
        }
      // println(i"rawBytes = \n$rawBytes%\n%") // DEBUG
      if (pickling ne noPrinter) {
        println(i"**** pickled info of $cls")
        new TastyPrinter(pickler.assembleParts()).printContents()
      }
    }
  }

  override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
    val result = super.runOn(units)
    if (ctx.settings.YtestPickler.value)
      testUnpickler(
          ctx.fresh
            .setPeriod(Period(ctx.runId + 1, FirstPhaseId))
            .addMode(Mode.ReadPositions))
    result
  }

  private def testUnpickler(implicit ctx: Context): Unit = {
    pickling.println(i"testing unpickler at run ${ctx.runId}")
    ctx.initialize()
    val unpicklers =
      for ((cls, pickler) <- picklers) yield {
        val unpickler = new DottyUnpickler(pickler.assembleParts())
        unpickler.enter(roots = Set())
        cls -> unpickler
      }
    pickling.println("************* entered toplevel ***********")
    for ((cls, unpickler) <- unpicklers) {
      val unpickled = unpickler.body
      testSame(i"$unpickled%\n%", beforePickling(cls), cls)
    }
  }

  private def testSame(unpickled: String, previous: String, cls: ClassSymbol)(implicit ctx: Context) =
    if (previous != unpickled) {
      output("before-pickling.txt", previous)
      output("after-pickling.txt", unpickled)
      ctx.error(s"""pickling difference for ${cls} in ${cls.sourceFile}, for details:
                   |
                   |  diff before-pickling.txt after-pickling.txt""")
    }
}