alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (Constructors.scala)

This example Scala source code file (Constructors.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

apply, assign, defdef, list, list, listbuffer, nosymbol, select, select, symbol, symbol, transformer, tree, tree

The Scala Constructors.scala source code

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

package scala.tools.nsc
package transform

import scala.collection.{ mutable, immutable }
import scala.collection.mutable.ListBuffer
import symtab.Flags._
import util.TreeSet

/** This phase converts classes with parameters into Java-like classes with 
 *  fields, which are assigned to from constructors.
 */  
abstract class Constructors extends Transform with ast.TreeDSL {
  import global._
  import definitions._

  /** the following two members override abstract members in Transform */
  val phaseName: String = "constructors"

  protected def newTransformer(unit: CompilationUnit): Transformer =
    new ConstructorTransformer(unit)

  private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = new mutable.HashMap[Symbol, List[Tree]]
  private val ctorParams: mutable.Map[Symbol, List[Symbol]] = new mutable.HashMap[Symbol, List[Symbol]]

  class ConstructorTransformer(unit: CompilationUnit) extends Transformer {

    def transformClassTemplate(impl: Template): Template = {
      val clazz = impl.symbol.owner  // the transformed class
      val stats = impl.body          // the transformed template body
      val localTyper = typer.atOwner(impl, clazz)

      val specializedFlag: Symbol = clazz.info.decl(nme.SPECIALIZED_INSTANCE)
      val shouldGuard = (specializedFlag != NoSymbol) && !clazz.hasFlag(SPECIALIZED)

      case class ConstrInfo(
        constr: DefDef,               // The primary constructor
        constrParams: List[Symbol],   // ... and its parameters
        constrBody: Block             // ... and its body
      )
      // decompose primary constructor into the three entities above.
      val constrInfo: ConstrInfo = {
        val primary = stats find (_.symbol.isPrimaryConstructor)
        assert(primary.isDefined, "no constructor in template: impl = " + impl)
        
        val ddef @ DefDef(_, _, _, List(vparams), _, rhs @ Block(_, _)) = primary.get
        ConstrInfo(ddef, vparams map (_.symbol), rhs)
      }
      import constrInfo._

      // The parameter accessor fields which are members of the class
      val paramAccessors = clazz.constrParamAccessors

      // The constructor parameter corresponding to an accessor
      def parameter(acc: Symbol): Symbol = 
        parameterNamed(nme.getterName(acc.originalName))

      // The constructor parameter with given name. This means the parameter
      // has given name, or starts with given name, and continues with a `$' afterwards.
      def parameterNamed(name: Name): Symbol = {
        def matchesName(param: Symbol) = param.name == name || param.name.startsWith(name + "$")
        
        (constrParams filter matchesName) match {
          case Nil    => assert(false, name + " not in " + constrParams) ; null
          case p :: _ => p
        }
      }

      var usesSpecializedField: Boolean = false

      // A transformer for expressions that go into the constructor
      val intoConstructorTransformer = new Transformer {
        def isParamRef(sym: Symbol) = 
          sym.isParamAccessor &&
          sym.owner == clazz &&
          !(clazz isSubClass DelayedInitClass) && 
          !(sym.isGetter && sym.accessed.isVariable) &&
          !sym.isSetter
        private def possiblySpecialized(s: Symbol) = specializeTypes.specializedTypeVars(s).nonEmpty
        override def transform(tree: Tree): Tree = tree match {
          case Apply(Select(This(_), _), List()) =>
            // references to parameter accessor methods of own class become references to parameters
            // outer accessors become references to $outer parameter 
            if (isParamRef(tree.symbol) && !possiblySpecialized(tree.symbol))
              gen.mkAttributedIdent(parameter(tree.symbol.accessed)) setPos tree.pos
            else if (tree.symbol.outerSource == clazz && !clazz.isImplClass)
              gen.mkAttributedIdent(parameterNamed(nme.OUTER)) setPos tree.pos
            else 
              super.transform(tree)
          case Select(This(_), _) if (isParamRef(tree.symbol) && !possiblySpecialized(tree.symbol)) => 
            // references to parameter accessor field of own class become references to parameters
            gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos
          case Select(_, _) =>
            if (specializeTypes.specializedTypeVars(tree.symbol).nonEmpty)
              usesSpecializedField = true
            super.transform(tree)
          case _ =>
            super.transform(tree)
        }
      }

      // Move tree into constructor, take care of changing owner from `oldowner' to constructor symbol
      def intoConstructor(oldowner: Symbol, tree: Tree) =
        intoConstructorTransformer.transform(
          new ChangeOwnerTraverser(oldowner, constr.symbol)(tree))

      // Should tree be moved in front of super constructor call?
      def canBeMoved(tree: Tree) = tree match {
        case ValDef(mods, _, _, _) => (mods hasFlag PRESUPER | PARAMACCESSOR)
        case _                     => false
      }

      // Create an assignment to class field `to' with rhs `from'
      def mkAssign(to: Symbol, from: Tree): Tree =
        localTyper.typedPos(to.pos) { Assign(Select(This(clazz), to), from) }

      // Create code to copy parameter to parameter accessor field. 
      // If parameter is $outer, check that it is not null so that we NPE
      // here instead of at some unknown future $outer access.
      def copyParam(to: Symbol, from: Symbol): Tree = {
        import CODE._
        val result = mkAssign(to, Ident(from))
        
        if (from.name != nme.OUTER) result
        else localTyper.typedPos(to.pos) {
          IF (from OBJ_EQ NULL) THEN THROW(NullPointerExceptionClass) ELSE result
        }
      }

      // The list of definitions that go into class
      val defBuf = new ListBuffer[Tree]
      
      // The auxiliary constructors, separate from the defBuf since they should
      // follow the primary constructor
      val auxConstructorBuf = new ListBuffer[Tree]

      // The list of statements that go into constructor after and including the superclass constructor call
      val constrStatBuf = new ListBuffer[Tree]

      // The list of early initializer statements that go into constructor before the superclass constructor call
      val constrPrefixBuf = new ListBuffer[Tree]

      // The early initialized field definitions of the class (these are the class members)
      val presupers = treeInfo.preSuperFields(stats)

      // generate code to copy pre-initialized fields
      for (stat <- constrBody.stats) {
        constrStatBuf += stat
        stat match {
          case ValDef(mods, name, _, _) if (mods hasFlag PRESUPER) =>
            // stat is the constructor-local definition of the field value
            val fields = presupers filter (
              vdef => nme.localToGetter(vdef.name) == name)
            assert(fields.length == 1)
            val to = fields.head.symbol
            if (!to.tpe.isInstanceOf[ConstantType])
              constrStatBuf += mkAssign(to, Ident(stat.symbol))
          case _ =>
        }
      }

      // Triage all template definitions to go into defBuf/auxConstructorBuf, constrStatBuf, or constrPrefixBuf.
      for (stat <- stats) stat match {
        case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
          // methods with constant result type get literals as their body
          // all methods except the primary constructor go into template
          stat.symbol.tpe match {
            case MethodType(List(), tp @ ConstantType(c)) =>
              defBuf += treeCopy.DefDef(
                stat, mods, name, tparams, vparamss, tpt,
                Literal(c) setPos rhs.pos setType tp)
            case _ =>
              if (stat.symbol.isPrimaryConstructor) ()
              else if (stat.symbol.isConstructor) auxConstructorBuf += stat
              else defBuf += stat
          }
        case ValDef(mods, name, tpt, rhs) =>
          // val defs with constant right-hand sides are eliminated.
          // for all other val defs, an empty valdef goes into the template and 
          // the initializer goes as an assignment into the constructor
          // if the val def is an early initialized or a parameter accessor, it goes
          // before the superclass constructor call, otherwise it goes after.
          // Lazy vals don't get the assignment in the constructor.
          if (!stat.symbol.tpe.isInstanceOf[ConstantType]) {
            if (rhs != EmptyTree && !stat.symbol.isLazy) {
              val rhs1 = intoConstructor(stat.symbol, rhs);
              (if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign(
                stat.symbol, rhs1)
            }
            defBuf += treeCopy.ValDef(stat, mods, name, tpt, EmptyTree)
          }
        case ClassDef(_, _, _, _) =>
          // classes are treated recursively, and left in the template
          defBuf += new ConstructorTransformer(unit).transform(stat)
        case _ =>
          // all other statements go into the constructor
          constrStatBuf += intoConstructor(impl.symbol, stat)
      }

      // ----------- avoid making fields for symbols that are not accessed --------------

      // A sorted set of symbols that are known to be accessed outside the primary constructor.
      val accessedSyms = new TreeSet[Symbol]((x, y) => x isLess y)

      // a list of outer accessor symbols and their bodies
      var outerAccessors: List[(Symbol, Tree)] = List()

      // Could symbol's definition be omitted, provided it is not accessed?
      // This is the case if the symbol is defined in the current class, and
      // ( the symbol is an object private parameter accessor field, or
      //   the symbol is an outer accessor of a final class which does not override another outer accessor. )
      def maybeOmittable(sym: Symbol) = sym.owner == clazz && (
        sym.isParamAccessor && sym.isPrivateLocal ||
        sym.isOuterAccessor && sym.owner.isFinal && sym.allOverriddenSymbols.isEmpty &&
        !(clazz isSubClass DelayedInitClass)
      )

      // Is symbol known to be accessed outside of the primary constructor,
      // or is it a symbol whose definition cannot be omitted anyway? 
      def mustbeKept(sym: Symbol) = !maybeOmittable(sym) || (accessedSyms contains sym)

      // A traverser to set accessedSyms and outerAccessors
      val accessTraverser = new Traverser {
        override def traverse(tree: Tree) = {
          tree match {
            case DefDef(_, _, _, _, _, body) 
            if (tree.symbol.isOuterAccessor && tree.symbol.owner == clazz && clazz.isFinal) =>
              log("outerAccessors += " + tree.symbol.fullName)
              outerAccessors ::= (tree.symbol, body)
            case Select(_, _) =>
              if (!mustbeKept(tree.symbol)) {
                log("accessedSyms += " + tree.symbol.fullName)
                accessedSyms addEntry tree.symbol
              }
              super.traverse(tree)
            case _ =>
              super.traverse(tree)
          }
        }
      }

      // first traverse all definitions except outeraccesors 
      // (outeraccessors are avoided in accessTraverser)
      for (stat <- defBuf.iterator ++ auxConstructorBuf.iterator)
        accessTraverser.traverse(stat) 

      // then traverse all bodies of outeraccessors which are accessed themselves
      // note: this relies on the fact that an outer accessor never calls another
      // outer accessor in the same class.
      for ((accSym, accBody) <- outerAccessors) 
        if (mustbeKept(accSym)) accessTraverser.traverse(accBody)

      // Conflicting symbol list from parents: see bug #1960.
      // It would be better to mangle the constructor parameter name since
      // it can only be used internally, but I think we need more robust name
      // mangling before we introduce more of it.
      val parentSymbols = Map((for {
        p <- impl.parents
        if p.symbol.isTrait
        sym <- p.symbol.info.nonPrivateMembers
        if sym.isGetter && !sym.isOuterField
      } yield sym.name -> p): _*)

      // Initialize all parameters fields that must be kept.
      val paramInits = 
        for (acc <- paramAccessors if mustbeKept(acc)) yield {          
          if (parentSymbols contains acc.name)
            unit.error(acc.pos, "parameter '%s' requires field but conflicts with %s in '%s'".format(
              acc.name, acc.name, parentSymbols(acc.name)))
          
          copyParam(acc, parameter(acc))
        }

      /** Return a single list of statements, merging the generic class constructor with the
       *  specialized stats. The original statements are retyped in the current class, and
       *  assignments to generic fields that have a corresponding specialized assignment in
       *  `specializedStats` are replaced by the specialized assignment.
       */
      def mergeConstructors(genericClazz: Symbol, originalStats: List[Tree], specializedStats: List[Tree]): List[Tree] = {
        val specBuf = new ListBuffer[Tree]
        specBuf ++= specializedStats

        def specializedAssignFor(sym: Symbol): Option[Tree] =
          specializedStats.find {
            case Assign(sel @ Select(This(_), _), rhs) if sel.symbol.hasFlag(SPECIALIZED) =>
              val (generic, _, _) = nme.splitSpecializedName(nme.localToGetter(sel.symbol.name))
              generic == nme.localToGetter(sym.name)
            case _ => false
          }

        /** Rewrite calls to ScalaRunTime.array_update to the proper apply method in scala.Array.
         *  Erasure transforms Array.update to ScalaRunTime.update when the element type is a type
         *  variable, but after specialization this is a concrete primitive type, so it would
         *  be an error to pass it to array_update(.., .., Object).
         */
        def rewriteArrayUpdate(tree: Tree): Tree = {
          val array_update = definitions.ScalaRunTimeModule.info.member("array_update")
          val adapter = new Transformer {
            override def transform(t: Tree): Tree = t match {
              case Apply(fun @ Select(receiver, method), List(xs, idx, v)) if fun.symbol == array_update =>
                localTyper.typed(Apply(gen.mkAttributedSelect(xs, definitions.Array_update), List(idx, v)))
              case _ => super.transform(t)
            }
          }
          adapter.transform(tree)
        }

        log("merging: " + originalStats.mkString("\n") + "\nwith\n" + specializedStats.mkString("\n"))
        val res = for (s <- originalStats; val stat = s.duplicate) yield {
          log("merge: looking at " + stat)
          val stat1 = stat match {
            case Assign(sel @ Select(This(_), field), _) =>
              specializedAssignFor(sel.symbol).getOrElse(stat)
            case _ => stat
          }
          if (stat1 ne stat) {
            log("replaced " + stat + " with " + stat1)
            specBuf -= stat1
          }

          if (stat1 eq stat) {
            assert(ctorParams(genericClazz).length == constrParams.length)
            // this is just to make private fields public
            (new specializeTypes.ImplementationAdapter(ctorParams(genericClazz), constrParams, null, true))(stat1)

            val stat2 = rewriteArrayUpdate(stat1)
            // statements coming from the original class need retyping in the current context
            if (settings.debug.value) log("retyping " + stat2)
            
            val d = new specializeTypes.Duplicator
            d.retyped(localTyper.context1.asInstanceOf[d.Context],
                      stat2,
                      genericClazz,
                      clazz,
                      Map.empty)
          } else
            stat1
        }
        if (specBuf.nonEmpty)
          println("residual specialized constructor statements: " + specBuf)
        res
      }

      /** Add an 'if' around the statements coming after the super constructor. This
       *  guard is necessary if the code uses specialized fields. A specialized field is
       *  initialized in the subclass constructor, but the accessors are (already) overridden
       *  and pointing to the (empty) fields. To fix this, a class with specialized fields
       *  will not run its constructor statements if the instance is specialized. The specialized
       *  subclass includes a copy of those constructor statements, and runs them. To flag that a class
       *  has specialized fields, and their initialization should be deferred to the subclass, method
       *  'specInstance$' is added in phase specialize.
       */
      def guardSpecializedInitializer(stats: List[Tree]): List[Tree] = if (settings.nospecialization.value) stats else {
        // split the statements in presuper and postsuper
    //    var (prefix, postfix) = stats0.span(tree => !((tree.symbol ne null) && tree.symbol.isConstructor))
      //  if (postfix.nonEmpty) {
        //  prefix = prefix :+ postfix.head
          //postfix = postfix.tail
        //}

        if (usesSpecializedField && shouldGuard && stats.nonEmpty) {
          // save them for duplication in the specialized subclass
          guardedCtorStats(clazz) = stats
          ctorParams(clazz) = constrParams

          val tree =
            If(
              Apply(
                Select(
                  Apply(gen.mkAttributedRef(specializedFlag), List()),
                  definitions.getMember(definitions.BooleanClass, nme.UNARY_!)),
                List()),
              Block(stats, Literal(())),
              EmptyTree)

          List(localTyper.typed(tree))
        } else if (clazz.hasFlag(SPECIALIZED)) {
          // add initialization from its generic class constructor
          val (genericName, _, _) = nme.splitSpecializedName(clazz.name)
          val genericClazz = clazz.owner.info.decl(genericName.toTypeName)
          assert(genericClazz != NoSymbol)

          guardedCtorStats.get(genericClazz) match {
            case Some(stats1) => mergeConstructors(genericClazz, stats1, stats)
            case None => stats
          }
        } else stats
      }
/*
      def isInitDef(stat: Tree) = stat match {
        case dd: DefDef => dd.symbol == delayedInitMethod
        case _ => false
      }
*/
      
      /** Create a getter or a setter and enter into `clazz` scope
       */
      def addAccessor(sym: Symbol, name: TermName, flags: Long) = {
        val m = clazz.newMethod(sym.pos, name)
          .setFlag(flags & ~LOCAL & ~PRIVATE)
        m.privateWithin = clazz
        clazz.info.decls.enter(m)
        m
      }
      
      def addGetter(sym: Symbol): Symbol = {
        val getr = addAccessor(
          sym, nme.getterName(sym.name), getterFlags(sym.flags))
        getr setInfo MethodType(List(), sym.tpe)
        defBuf += localTyper.typed {
          //util.trace("adding getter def for "+getr) {
          atPos(sym.pos) {
            DefDef(getr, Select(This(clazz), sym))
          }//}
        }
        getr
      }
      
      def addSetter(sym: Symbol): Symbol = {
        sym setFlag MUTABLE
        val setr = addAccessor(
          sym, nme.getterToSetter(nme.getterName(sym.name)), setterFlags(sym.flags)) 
        setr setInfo MethodType(setr.newSyntheticValueParams(List(sym.tpe)), UnitClass.tpe)
        defBuf += localTyper.typed {
          //util.trace("adding setter def for "+setr) {
          atPos(sym.pos) {
            DefDef(setr, paramss => 
              Assign(Select(This(clazz), sym), Ident(paramss.head.head)))
          }//}
        }
        setr
      }
      
      def ensureAccessor(sym: Symbol)(acc: => Symbol) = 
        if (sym.owner == clazz && !sym.isMethod && sym.isPrivate) { // there's an access to a naked field of the enclosing class
          var getr = acc
          getr makeNotPrivate clazz
          getr
        } else {
          if (sym.owner == clazz) sym makeNotPrivate clazz
          NoSymbol
        }
      
      def ensureGetter(sym: Symbol): Symbol = ensureAccessor(sym) {
        val getr = sym.getter(clazz)
        if (getr != NoSymbol) getr else addGetter(sym)
      }
      
      def ensureSetter(sym: Symbol): Symbol = ensureAccessor(sym) {
        var setr = sym.setter(clazz, hasExpandedName = false)
        if (setr == NoSymbol) setr = sym.setter(clazz, hasExpandedName = true)
        if (setr == NoSymbol) setr = addSetter(sym)
        setr
      }
      
      def delayedInitClosure(stats: List[Tree]) = 
        localTyper.typed {
          atPos(impl.pos) { 
            val closureClass = clazz.newClass(impl.pos, nme.delayedInitArg.toTypeName)
              .setFlag(SYNTHETIC | FINAL)
            val closureParents = List(AbstractFunctionClass(0).tpe, ScalaObjectClass.tpe)
            closureClass.setInfo(new ClassInfoType(closureParents, new Scope, closureClass))

            val outerField = closureClass.newValue(impl.pos, nme.OUTER)
              .setFlag(PRIVATE | LOCAL | PARAMACCESSOR)
              .setInfo(clazz.tpe)

            val applyMethod = closureClass.newMethod(impl.pos, nme.apply)
              .setFlag(FINAL)
              .setInfo(MethodType(List(), ObjectClass.tpe))

            closureClass.info.decls enter outerField
            closureClass.info.decls enter applyMethod

            val outerFieldDef = ValDef(outerField)

            val changeOwner = new ChangeOwnerTraverser(impl.symbol, applyMethod)

            val closureClassTyper = localTyper.atOwner(closureClass)
            val applyMethodTyper = closureClassTyper.atOwner(applyMethod)

            val constrStatTransformer = new Transformer {
              override def transform(tree: Tree): Tree = tree match {
                case This(_) if tree.symbol == clazz => 
                  applyMethodTyper.typed {
                    atPos(tree.pos) {
                      Select(This(closureClass), outerField)
                    }
                  }
                case _ =>
                  super.transform {
                    tree match {
                      case Select(qual, _) => 
                        val getter = ensureGetter(tree.symbol)
                        if (getter != NoSymbol)
                          applyMethodTyper.typed {
                            atPos(tree.pos) {
                              Apply(Select(qual, getter), List())
                            }
                          }
                        else tree
                      case Assign(lhs @ Select(qual, _), rhs) =>
                        val setter = ensureSetter(lhs.symbol)
                        if (setter != NoSymbol)
                          applyMethodTyper.typed {
                            atPos(tree.pos) {
                              Apply(Select(qual, setter), List(rhs))
                            }
                          }
                        else tree
                      case _ => 
                        changeOwner.changeOwner(tree)
                        tree
                    }
                  } 
              }
            }

            def applyMethodStats = constrStatTransformer.transformTrees(stats)

            val applyMethodDef = DefDef(
              sym = applyMethod,
              vparamss = List(List()),
              rhs = Block(applyMethodStats, gen.mkAttributedRef(BoxedUnit_UNIT)))
              
            ClassDef(
              sym = closureClass,
              constrMods = Modifiers(0),
              vparamss = List(List(outerFieldDef)),
              argss = List(List()),
              body = List(applyMethodDef),
              superPos = impl.pos)
          }
        }

      def delayedInitCall(closure: Tree) = 
        localTyper.typed {
          atPos(impl.pos) { 
            Apply(
              Select(This(clazz), delayedInitMethod), 
              List(New(TypeTree(closure.symbol.tpe), List(List(This(clazz))))))
          }
        }

      /** Return a pair consisting of (all statements up to and including superclass and trait constr calls, rest) */
      def splitAtSuper(stats: List[Tree]) = {
        def isConstr(tree: Tree) = (tree.symbol ne null) && tree.symbol.isConstructor
        val (pre, rest0) = stats span (!isConstr(_))
        val (supercalls, rest) = rest0 span (isConstr(_))
        (pre ::: supercalls, rest)
      }

      var (uptoSuperStats, remainingConstrStats) = splitAtSuper(constrStatBuf.toList)

      val needsDelayedInit =
        (clazz isSubClass DelayedInitClass) /*&& !(defBuf exists isInitDef)*/ && remainingConstrStats.nonEmpty

      if (needsDelayedInit) {
        val dicl = new ConstructorTransformer(unit) transform delayedInitClosure(remainingConstrStats)
        defBuf += dicl
        remainingConstrStats = List(delayedInitCall(dicl))
      }

      // Assemble final constructor
      defBuf += treeCopy.DefDef(
        constr, constr.mods, constr.name, constr.tparams, constr.vparamss, constr.tpt,
        treeCopy.Block(
          constrBody,
          paramInits ::: constrPrefixBuf.toList ::: uptoSuperStats ::: 
            guardSpecializedInitializer(remainingConstrStats),
          constrBody.expr));

      // Followed by any auxiliary constructors
      defBuf ++= auxConstructorBuf
      
      // Unlink all fields that can be dropped from class scope
      for (sym <- clazz.info.decls.toList) 
        if (!mustbeKept(sym)) {
          // println("dropping "+sym+sym.locationString)
          clazz.info.decls unlink sym
        }

      // Eliminate all field definitions that can be dropped from template
      treeCopy.Template(impl, impl.parents, impl.self, 
        defBuf.toList filter (stat => mustbeKept(stat.symbol)))
    } // transformClassTemplate

    override def transform(tree: Tree): Tree = 
      tree match {
        case ClassDef(mods, name, tparams, impl) if !tree.symbol.isInterface && !isValueClass(tree.symbol) =>
          treeCopy.ClassDef(tree, mods, name, tparams, transformClassTemplate(impl))
        case _ =>
          super.transform(tree)
      }
  } // ConstructorTransformer
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala Constructors.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.