syntheticmethods.scala

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

SCALA
321
字号
/* NSC -- new Scala compiler * Copyright 2005-2007 LAMP/EPFL * @author Martin Odersky */// $Id: SyntheticMethods.scala 14550 2008-04-08 10:54:54Z odersky $package scala.tools.nsc.typecheckerimport symtab.Flags._import scala.collection.mutable.ListBuffer/** <ul> *    <li> *      <code>productArity</code>, <code>element</code> implementations added *      to case classes *    </li> *    <li> *      <code>equals</code>, <code>hashCode</code> and </code>toString</code> *      methods are added to case classes, unless they are defined in the *      class or a baseclass different from <code>java.lang.Object</code> *    </li> *    <li> *      <code>toString</code> method is added to case objects, unless they *      are defined in the class or a baseclass different from *      <code>java.lang.Object</code> *    </li> *  </ul> */trait SyntheticMethods { self: Analyzer =>  import global._                  // the global environment  import definitions._             // standard classes and methods  //import global.typer.{typed}      // methods to type trees  // @S: type hack: by default, we are used from global.analyzer context  // so this cast won't fail. If we aren't in global.analyzer, we have  // to override this method anyways.  protected def typer : Typer = global.typer.asInstanceOf[Typer]  /**   *  @param templ ...   *  @param clazz ...   *  @param unit  ...   *  @return      ...   */  def addSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = try {    val localContext = if (reporter.hasErrors) context.makeSilent(false) else context    val localTyper = newTyper(localContext)    def hasImplementation(name: Name): Boolean = if (inIDE) true else {      val sym = clazz.info.nonPrivateMember(name)       sym.isTerm && !(sym hasFlag DEFERRED)    }    def hasOverridingImplementation(meth: Symbol): Boolean = if (inIDE) true else {      val sym = clazz.info.nonPrivateMember(meth.name)      sym.alternatives exists { sym =>        sym != meth && !(sym hasFlag DEFERRED) && !(sym hasFlag (SYNTHETIC | SYNTHETICMETH)) &&         (clazz.thisType.memberType(sym) matches clazz.thisType.memberType(meth))      }    }    def syntheticMethod(name: Name, flags: Int, tpe: Type) =      newSyntheticMethod(name, flags | OVERRIDE, tpe)    def newSyntheticMethod(name: Name, flags: Int, tpe: Type) = {      var method = clazz.newMethod(clazz.pos, name)         .setFlag(flags | (if (inIDE) SYNTHETIC else SYNTHETICMETH))        .setInfo(tpe)      method = clazz.info.decls.enter(method).asInstanceOf[TermSymbol]      method    }    /*    def productSelectorMethod(n: int, accessor: Symbol): Tree = {      val method = syntheticMethod(newTermName("_"+n), FINAL, accessor.tpe)       typed(DefDef(method, vparamss => gen.mkAttributedRef(accessor)))    }    */    def productPrefixMethod: Tree = {      val method = syntheticMethod(nme.productPrefix, 0, PolyType(List(), StringClass.tpe))      typer.typed(DefDef(method, vparamss => Literal(Constant(clazz.name.decode))))    }    def productArityMethod(nargs:Int ): Tree = {      val method = syntheticMethod(nme.productArity, 0, PolyType(List(), IntClass.tpe))      typer.typed(DefDef(method, vparamss => Literal(Constant(nargs))))    }    def productElementMethod(accs: List[Symbol]): Tree = {      //val retTpe = lub(accs map (_.tpe.resultType))      val method = syntheticMethod(nme.productElement, 0, MethodType(List(IntClass.tpe), AnyClass.tpe/*retTpe*/))      typer.typed(DefDef(method, vparamss => Match(Ident(vparamss.head.head), {	(for ((sym,i) <- accs.zipWithIndex) yield {	  CaseDef(Literal(Constant(i)),EmptyTree, Ident(sym))	}):::List(CaseDef(Ident(nme.WILDCARD), EmptyTree, 		    Throw(New(TypeTree(IndexOutOfBoundsExceptionClass.tpe), List(List(		      Select(Ident(vparamss.head.head), nme.toString_)		    ))))))      })))    }    def moduleToStringMethod: Tree = {      val method = syntheticMethod(nme.toString_, FINAL, MethodType(List(), StringClass.tpe))      typer.typed(DefDef(method, vparamss => Literal(Constant(clazz.name.decode))))    }    def tagMethod: Tree = {      val method = syntheticMethod(nme.tag, 0, MethodType(List(), IntClass.tpe))      typer.typed(DefDef(method, vparamss => Literal(Constant(clazz.tag))))    }    def forwardingMethod(name: Name): Tree = {      val target = getMember(ScalaRunTimeModule, "_" + name)      val paramtypes =        if (target.tpe.paramTypes.isEmpty) List()        else target.tpe.paramTypes.tail      val method = syntheticMethod(        name, 0, MethodType(paramtypes, target.tpe.resultType))      typer.typed(DefDef(method, vparamss =>        Apply(gen.mkAttributedRef(target), This(clazz) :: (vparamss.head map Ident))))    }    /** The equality method for case classes and modules:     *   def equals(that: Any) =      *     that.isInstanceOf[AnyRef] &&     *     ((this eq that.asInstanceOf[AnyRef]) ||      *     (that match {     *       case this.C(this.arg_1, ..., this.arg_n) => true     *       case _ => false     *     }))     */    def equalsMethod: Tree = {      val method = syntheticMethod(        nme.equals_, 0, MethodType(List(AnyClass.tpe), BooleanClass.tpe))      val methodDef =         DefDef(          method,           { vparamss =>            val that = Ident(vparamss.head.head)            val constrParamTypes = clazz.primaryConstructor.tpe.paramTypes            val hasVarArgs = !constrParamTypes.isEmpty && constrParamTypes.last.typeSymbol == RepeatedParamClass            if (false && clazz.isStatic) {              // todo: elim              val target = getMember(ScalaRunTimeModule, if (hasVarArgs) nme._equalsWithVarArgs else nme._equals)              Apply(                Select(                  TypeApply(                    Select(that, Any_isInstanceOf),                    List(TypeTree(clazz.tpe))),                  Boolean_and),                List(                  Apply(gen.mkAttributedRef(target),                        This(clazz) :: (vparamss.head map Ident))))            } else {              val (pat, guard) = {                val guards = new ListBuffer[Tree]                val params = for ((acc, cpt) <- clazz.caseFieldAccessors zip constrParamTypes) yield {                   val name = context.unit.fresh.newName(clazz.pos, acc.name+"$")                  val isVarArg = cpt.typeSymbol == RepeatedParamClass                   guards += Apply(                    Select(                      Ident(name),                      if (isVarArg) nme.sameElements else nme.EQEQ),                    List(Ident(acc)))                  Bind(name,                        if (isVarArg) Star(Ident(nme.WILDCARD))                       else Ident(nme.WILDCARD))                }                ( Apply(Ident(clazz.name.toTermName), params),                  if (guards.isEmpty) EmptyTree                  else guards reduceLeft { (g1: Tree, g2: Tree) =>                    Apply(Select(g1, nme.AMPAMP), List(g2))                  }                )              }              val isAnyRef = TypeApply(                    Select(that, Any_isInstanceOf),                    List(TypeTree(AnyRefClass.tpe)))              val cast = TypeApply(                    Select(that, Any_asInstanceOf),                    List(TypeTree(AnyRefClass.tpe)))              val eq_ = Apply(Select( This(clazz) , nme.eq), List(that setType AnyRefClass.tpe))               val match_ = Match(that, List(                    CaseDef(pat, guard, Literal(Constant(true))),                    CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))))              Apply(                    Select(isAnyRef, Boolean_and),                    List(Apply(Select(eq_, Boolean_or),                    List(match_))))            }          }        )      localTyper.typed(methodDef)    }    def isSerializable(clazz: Symbol): Boolean =      !clazz.getAttributes(definitions.SerializableAttr).isEmpty    def readResolveMethod: Tree = {      // !!! the synthetic method "readResolve" should be private,      // but then it is renamed !!!      val method = newSyntheticMethod(nme.readResolve, PROTECTED,                                      MethodType(List(), ObjectClass.tpe))      typer.typed(DefDef(method, vparamss => gen.mkAttributedRef(clazz.sourceModule)))    }    def newAccessorMethod(tree: Tree): Tree = tree match {      case DefDef(_, _, _, _, _, rhs) =>        var newAcc = tree.symbol.cloneSymbol        newAcc.name = context.unit.fresh.newName(tree.symbol.pos, tree.symbol.name + "$")        newAcc.setFlag(SYNTHETIC).resetFlag(ACCESSOR | PARAMACCESSOR | PRIVATE)        newAcc = newAcc.owner.info.decls enter newAcc        val result = typer.typed(DefDef(newAcc, vparamss => rhs.duplicate))        log("new accessor method " + result)        result    }    def beanSetterOrGetter(sym: Symbol): Symbol =      if (!sym.name(0).isLetter) {        context.unit.error(sym.pos, "attribute `BeanProperty' can be applied only to fields that start with a letter")        NoSymbol      } else {        var name0 = sym.name        if (sym.isSetter) name0 = nme.setterToGetter(name0)        val prefix = if (sym.isSetter) "set" else          if (sym.tpe.resultType == BooleanClass.tpe) "is" else "get"        val arity = if (sym.isSetter) 1 else 0        val name1 = prefix + name0(0).toUpperCase + name0.subName(1, name0.length)        val sym1 = clazz.info.decl(name1)        if (sym1 != NoSymbol && sym1.tpe.paramTypes.length == arity) {          context.unit.error(sym.pos, "a definition of `"+name1+"' already exists in " + clazz)          NoSymbol        } else {          clazz.newMethod(sym.pos, name1)            .setInfo(sym.info)            .setFlag(sym.getFlag(DEFERRED | OVERRIDE | STATIC))        }      }    val ts = new ListBuffer[Tree]    def addBeanGetterMethod(sym: Symbol) = {      val getter = beanSetterOrGetter(sym)      if (getter != NoSymbol)        ts += typer.typed(DefDef(          getter,          vparamss => if (sym hasFlag DEFERRED) EmptyTree else gen.mkAttributedRef(sym)))    }    def addBeanSetterMethod(sym: Symbol) = {      val setter = beanSetterOrGetter(sym)      if (setter != NoSymbol)        ts += typer.typed(DefDef(          setter,          vparamss =>            if (sym hasFlag DEFERRED) EmptyTree             else Apply(gen.mkAttributedRef(sym), List(Ident(vparamss.head.head)))))    }    def isPublic(sym: Symbol) =       !sym.hasFlag(PRIVATE | PROTECTED) && sym.privateWithin == NoSymbol    if (!phase.erasedTypes) {      try {        if (clazz hasFlag CASE) {          val isTop = !(clazz.info.baseClasses.tail exists (_ hasFlag CASE))          // case classes are implicitly declared serializable          clazz.attributes = AnnotationInfo(SerializableAttr.tpe, List(), List()) :: clazz.attributes          if (isTop) {            for (stat <- templ.body) {              if (stat.isDef && stat.symbol.isMethod && stat.symbol.hasFlag(CASEACCESSOR) && !isPublic(stat.symbol)) {                ts += newAccessorMethod(stat)                stat.symbol.resetFlag(CASEACCESSOR)              }            }            if (!inIDE && !clazz.hasFlag(INTERFACE) && clazz.info.nonPrivateDecl(nme.tag) == NoSymbol) ts += tagMethod          }          if (clazz.isModuleClass) {            if (!hasOverridingImplementation(Object_toString)) ts += moduleToStringMethod          } else {            if (!hasOverridingImplementation(Object_hashCode)) ts += forwardingMethod(nme.hashCode_)            if (!hasOverridingImplementation(Object_toString)) ts += forwardingMethod(nme.toString_)            if (!hasOverridingImplementation(Object_equals)) ts += equalsMethod          }          if (!hasOverridingImplementation(Product_productPrefix)) ts += productPrefixMethod          val accessors = clazz.caseFieldAccessors          if (!hasOverridingImplementation(Product_productArity))            ts += productArityMethod(accessors.length)          if (!hasOverridingImplementation(Product_productElement))            ts += productElementMethod(accessors)        }        if (clazz.isModuleClass && isSerializable(clazz)) {          // If you serialize a singleton and then deserialize it twice,          // you will have two instances of your singleton, unless you implement          // the readResolve() method (see http://www.javaworld.com/javaworld/          // jw-04-2003/jw-0425-designpatterns_p.html)          if (!hasImplementation(nme.readResolve)) ts += readResolveMethod        }        if (!forCLDC && !forMSIL)          for (sym <- clazz.info.decls.toList)            if (!sym.getAttributes(BeanPropertyAttr).isEmpty)              if (sym.isGetter)                addBeanGetterMethod(sym)              else if (sym.isSetter)                addBeanSetterMethod(sym)              else if (sym.isMethod || sym.isType)                context.unit.error(sym.pos, "attribute `BeanProperty' is not applicable to " + sym)      } catch {        case ex: TypeError =>          if (!reporter.hasErrors) throw ex      }    }    val synthetics = ts.toList    copy.Template(      templ, templ.parents, templ.self, if (synthetics.isEmpty) templ.body else templ.body ::: synthetics)  }}

⌨️ 快捷键说明

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