lazyvals.scala

来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 164 行

SCALA
164
字号
package scala.tools.nsc.transform;import scala.tools.nsc._import scala.collection.mutable.HashMapabstract class LazyVals extends Transform {  // inherits abstract value `global' and class `Phase' from Transform  import global._                  // the global environment  import definitions._             // standard classes and methods  import typer.{typed, atOwner}    // methods to type trees  import posAssigner.atPos         // for filling in tree positions   val phaseName: String = "lazyvals"  def newTransformer(unit: CompilationUnit): Transformer =    new LazyValues(unit)  /** Create a new phase which applies transformer */  override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = new Phase(prev)  /** The phase defined by this transform */  class Phase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) {    def apply(unit: global.CompilationUnit): Unit =        newTransformer(unit).transformUnit(unit);  }  /**   * Transform local lazy accessors to check for the initialized bit.   */  class LazyValues(unit: CompilationUnit) extends Transformer {        import definitions.{Int_And, Int_Or, Int_==}        /** map from method symbols to the number of lazy values it defines. */    private val lazyVals = new HashMap[Symbol, Int] {      override def default(meth: Symbol) = 0    }        import symtab.Flags._    import lazyVals._    /** Perform the following transformations:     *  - for a lazy accessor inside a method, make it check the initialization bitmap     *  - for all methods, add enough int vars to allow one flag per lazy local value     *  - remove ACCESSOR flags: accessors in traits are not statically implemented,       *    but moved to the host class. local lazy values should be statically implemented.     */    override def transform(tree: Tree): Tree = {      val sym = tree.symbol      tree match {        case DefDef(mods, name, tparams, vparams, tpt, rhs) =>          val res = if (!sym.owner.isClass && sym.hasFlag(LAZY)) {            val idx = lazyVals(sym.enclMethod)            val rhs1 = mkLazyDef(sym.enclMethod, super.transform(rhs), idx)            lazyVals(sym.owner) = idx + 1            sym.resetFlag(LAZY | ACCESSOR)             rhs1          } else            super.transform(rhs)          val bmps = bitmaps(sym) map { b => ValDef(b, Literal(Constant(0))) }          val tmp = addBitmapDefs(sym, res, bmps)          copy.DefDef(tree, mods, name, tparams, vparams, tpt,                       typed(addBitmapDefs(sym, res, bmps)))        case _ => super.transform(tree)      }    }    /** Add the bitmap definitions to the rhs of a method definition.     *  If the rhs has been tail-call trasnformed, insert the bitmap     *  definitions inside the top-level label definition, so that each     *  iteration has the lazy values un-initialized. Otherwise add them     *  at the very beginning of the method.     */    private def addBitmapDefs(methSym: Symbol, rhs: Tree, bmps: List[Tree]): Tree = {      if (bmps.isEmpty) rhs else rhs match {        case Block(assign, l @ LabelDef(name, params, rhs1))          if (name.toString.equals("_" + methSym.name)              && List.forall2(params.tail, methSym.tpe.paramTypes) { (ident, tpe) => ident.tpe == tpe }) =>            val sym = l.symbol            Block(assign, copy.LabelDef(l, name, params, typed(Block(bmps, rhs1))))        case _ => Block(bmps, rhs)      }    }        /** return a 'lazified' version of rhs. Rhs should conform to the     *  following schema:     *  {     *    l$ = <rhs>      *    l$     *  } or     *  <rhs> when the lazy value has type Unit (for which there is no field     *  to cache it's value.     *      *  The result will be a tree of the form     *  {     *    if ((bitmap$n & MASK) == 0) {     *       l$ = <rhs>     *       bitmap$n = bimap$n | MASK     *    }     *    l$     *  }     *  where bitmap$n is an int value acting as a bitmap of initialized values. It is     *  the 'n' is (offset / 32), the MASK is (1 << (offset % 32)). If the value has type     *  unit, no field is used to chache the value, so the resulting code is:     *  {     *    if ((bitmap$n & MASK) == 0) {     *       <rhs>;     *       bitmap$n = bimap$n | MASK     *    }     *    ()     *  }     */    private def mkLazyDef(meth: Symbol, tree: Tree, offset: Int): Tree = {      val bitmapSym = getBitmapFor(meth, offset)      val mask = Literal(Constant(1 << (offset % FLAGS_PER_WORD)))            val (block, res) = tree match {        case Block(List(assignment), res) =>          (Block(List(assignment, mkSetFlag(bitmapSym, mask)), Literal(Constant(()))), res)        case rhs =>          assert(meth.tpe.finalResultType.typeSymbol == definitions.UnitClass)          (Block(List(rhs, mkSetFlag(bitmapSym, mask)), Literal(Constant(()))), Literal(()))      }            val result = atPos(tree.pos) {        If(Apply(            Select(              Apply(Select(Ident(bitmapSym), Int_And),                     List(mask)),              Int_==),            List(Literal(Constant(0)))), block, EmptyTree)      }      typed(Block(List(result), res))    }         private def mkSetFlag(bmp: Symbol, mask: Tree): Tree =       Assign(Ident(bmp),        Apply(Select(Ident(bmp), Int_Or), List(mask)))            final val FLAGS_PER_WORD = 32    val bitmaps = new HashMap[Symbol, List[Symbol]] {      override def default(meth: Symbol) = Nil    }        /** Return the symbol corresponding of the right bitmap int inside meth,     *  given offset.     */    private def getBitmapFor(meth: Symbol, offset: Int): Symbol = {      val n = offset / FLAGS_PER_WORD      val bmps = bitmaps(meth)      if (bmps.length > n)         bmps(n)      else {        val sym = meth.newVariable(meth.pos, nme.bitmapName(n)).setInfo(IntClass.tpe)        bitmaps(meth) = (sym :: bmps).reverse        sym      }    }  }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?