加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 使用类上的宏注释创建或扩展伴随对象

发布时间:2020-12-16 09:28:58 所属栏目:安全 来源:网络整理
导读:使用Scala 2.10 / 2.11宏天堂注释宏,如何添加或扩展带注释类的伴随对象?骨架: import scala.annotation.StaticAnnotationimport scala.reflect.macros._import language.experimental.macrosclass foo extends StaticAnnotation { def macroTransform(anno
使用Scala 2.10 / 2.11宏天堂注释宏,如何添加或扩展带注释类的伴随对象?骨架:

import scala.annotation.StaticAnnotation
import scala.reflect.macros._
import language.experimental.macros

class foo extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro fooMacro.impl
}
object fooMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = ???
}

这样,给定

trait Foo[A]

以下输入

@foo class Bar

object Baz {
  def baz = 33
}
@foo class Baz

将扩展为:

object Bar {
  implicit def hasFoo: Foo[Bar] = ???
}
class Bar

object Baz {
  def baz = 33

  implicit def hasFoo: Foo[Baz] = ???
}
class Baz

这是第一次天真的尝试,现在只需添加def hasFoo = 33:

def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
  import c.universe._
  val inputs : List[Tree] = annottees.map(_.tree)(collection.breakOut)
  val outputs: List[Tree] = inputs match {
    case (cd @ ClassDef(_,cName,_,_)) :: tail =>
      val mod0: ModuleDef = tail match {
        case (md @ ModuleDef(_,mName,_)) :: Nil
             if cName.decoded == mName.decoded => md
        case Nil =>
          val cMod = cd.mods
          var mModF = NoFlags
          if (cMod hasFlag Flag.PRIVATE  ) mModF |= Flag.PRIVATE
          if (cMod hasFlag Flag.PROTECTED) mModF |= Flag.PROTECTED
          if (cMod hasFlag Flag.LOCAL    ) mModF |= Flag.LOCAL
          val mMod = Modifiers(mModF,cMod.privateWithin,Nil)
          // or should we have parents = List(AnyRef) and body = List(DefDef(???))
          val mTemp = Template(parents = Nil,self = noSelfType,body = Nil)
          val mName = TermName(cName.decoded) // or encoded?
          ModuleDef(mMod,mTemp)
        case _ => c.abort(c.enclosingPosition,"Expected a companion object")
      }
      val Template(mTempParents,mTempSelf,mTempBody0) = mod0.impl
      val fooDef = DefDef(NoMods,TermName("hasFoo"),Nil,TypeTree(typeOf[Int]),Literal(Constant(33)))
      val mTempBody1 = fooDef :: mTempBody0
      val mTemp1 = Template(mTempParents,mTempBody1)
      val mod1 = ModuleDef(mod0.mods,mod0.name,mTemp1)
      cd :: mod1 :: Nil

    case _ => c.abort(c.enclosingPosition,"Must annotate a class or trait")
  }
  c.Expr[Any](Block(outputs,Literal(Constant(()))))
}

当伴侣对象已存在时,此方法有效:

object Foo
@mkCompanion class Foo

assert(Foo.hasFoo == 33)

但不是在创建时:

@mkCompanion class Foo

[error] no constructor in template: impl = Object {
[error]   def hasFoo(): Int = 33
[error] }

所以我仍然需要弄清楚如何提供模块构造函数……

解决方法

这是我目前的解决方案:

import scala.annotation.StaticAnnotation
import scala.reflect.macros._
import language.experimental.macros

trait Foo[A]

class mkCompanion extends StaticAnnotation {
  def macroTransform(annottees: Any*) = macro mkCompanionMacro.impl
}
object mkCompanionMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    val inputs : List[Tree] = annottees.map(_.tree)(collection.breakOut)
    val outputs: List[Tree] = inputs match {
      case (cd @ ClassDef(_,_)) :: tail =>
        val mod0: ModuleDef = tail match {
          case (md @ ModuleDef(_,mTemp)) :: Nil 
               if cName.decoded == mName.decoded => md

          case Nil =>
            val cMod  = cd.mods
            var mModF = NoFlags
            if (cMod hasFlag Flag.PRIVATE  ) mModF |= Flag.PRIVATE
            if (cMod hasFlag Flag.PROTECTED) mModF |= Flag.PROTECTED
            if (cMod hasFlag Flag.LOCAL    ) mModF |= Flag.LOCAL
            val mMod = Modifiers(mModF,Nil)

            // XXX TODO: isn't there a shortcut for creating the constructor?
            val mkSuperSelect = Select(Super(This(tpnme.EMPTY),tpnme.EMPTY),nme.CONSTRUCTOR)
            val superCall     = Apply(mkSuperSelect,Nil)
            val constr        = DefDef(NoMods,nme.CONSTRUCTOR,List(Nil),TypeTree(),Block(List(superCall),Literal(Constant())))

            val mTemp = Template(parents = List(TypeTree(typeOf[AnyRef])),body = constr :: Nil)
            val mName = TermName(cName.decoded) // or encoded?

            ModuleDef(mMod,mTemp)

          case _ => c.abort(c.enclosingPosition,"Expected a companion object")
        }

        val Template(mTempParents,mTempBody0) = mod0.impl

        // cf. http://stackoverflow.com/questions/21044957/type-of-a-macro-annottee
        val cTpe        = Ident(TypeName(cd.name.decoded))
        val fooName     = TermName("hasFoo")
        val fooDef      = q"implicit def $fooName: Foo[$cTpe] = ???"
        val mTempBody1  = fooDef :: mTempBody0
        val mTemp1      = Template(mTempParents,mTempBody1)
        val mod1        = ModuleDef(mod0.mods,mTemp1)

        cd :: mod1 :: Nil

      case _ => c.abort(c.enclosingPosition,"Must annotate a class or trait")
    }

    c.Expr[Any](Block(outputs,Literal(Constant(()))))
  }
}

测试:

object Bar
@mkCompanion class Bar

@mkCompanion class Baz

implicitly[Foo[Bar]]
implicitly[Foo[Baz]]

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读