如何获取宏定义中提到的类的反射数据?

时间:2015-10-06 02:36:53

标签: scala reflection macros

我正在使用白盒宏注释来生成代码。我希望超越琐碎的quasiquote替换并根据额外信息决定宏:

  • 检查实际的条款类型
  • 查找包含继承的特征的所有成员

我知道当调用宏扩展时,当前处理的文件仍无法用于反射。它仍然是AST形式。

但是,所有相关文件应该已经编译好了。因此,如果传递给宏定义的AST具有名为MyType的类型,那么该类型应该已经在某处声明。令人遗憾的是,MyType可能是import语句中引入的别名。

所以,我认为应该解决以下问题以制作丰富的宏:

  1. 从给定的术语计算其规范名称
  2. 检查当前文件中是否定义了该名称并获取相应的SymTree
  3. 在另一种情况下,获取包含已编译文件的项目类路径,并在其中搜索规范名称。
  4. 而且,我不知道如何处理每项任务。你能建议一些出路吗?

    代码示例

    相当长的代码示例,但仍然很少。编程宏非常详细

    import scala.language.experimental.macros
    
    import scala.reflect.macros.Context
    import scala.annotation.StaticAnnotation
    import scala.annotation.compileTimeOnly
    
    import scala.collection.mutable.Buffer
    
    trait Companion {
      val ribbon : Array[Int]
    }
    
    trait Holder {
      sealed trait AnyNode {
        private[Holder] var _index : Int = 0
        def index : Int = _index
    
        //example use of companion
        def next : AnyNode =
          nodes( companion.ribbon(_index) )
      }
    
      class Node[T](default : T) extends AnyNode {
        private var _value : T = default
        def value : T = _value
        def value_=(nv : T) = {
          _value = nv
        }
        override def toString : String = s"Node( ${this._value} )"
      }
    
      def companion : Companion // override in macros
      protected val nodes : Array[AnyNode] // override in macros
    
      // accessors for generated subTrait
      protected def placeNode(node : AnyNode, position : Int) {
        node._index = position
        nodes(position) = node
      }
    
      //debug output
      def printNodes : String = "nodes: " + nodes.mkString("; ")
    }
    
    @compileTimeOnly("enable macro paradise to expand macro annotations")
    class fillHolder extends StaticAnnotation {
      def macroTransform(annottees: Any*) : Any = macro Implementation.fillHolder
    }
    
    class Implementation[C <: Context](val c : C) {
      import c.universe._
    
      lazy val typeNode = TypeName("Node")
      lazy val typeAnyNode = TypeName("AnyNode")
      lazy val typeCompanion = TypeName("Companion")
      lazy val typeHolder = TypeName("Holder")
      lazy val termCompanion = TermName("companion")
      lazy val termNodes = TermName("nodes")
      lazy val termRibbon = TermName("ribbon")
      lazy val termPlaceNode = TermName("placeNode")
    
      lazy val position = c.enclosingPosition
    
      def error(msg : String) =
        c.error(position, msg)
      def info(msg : String) =
        c.info(position, msg, true)
      def warn(msg : String) =
        c.warning(position, msg)
    
      def escape = c.Expr[Any](EmptyTree)
    
      def makeCompanion(name : TypeName) : ModuleDef = {
        val tname = name.toTermName
        q"object $tname extends $typeCompanion"
      }
    
      val extractHolder : PartialFunction[List[Tree], (ClassDef, ModuleDef)] = {
        case (holder : ClassDef) :: Nil => ( holder, makeCompanion(holder.name) )
        case (holder : ClassDef) :: (comp : ModuleDef) :: Nil => ( holder, comp )
      }
    
      def detectMethod(impl : Template, name : TermName) : Boolean = {
        impl.body.exists {
          case vd : ValDef => vd.name == name
          case dd : DefDef => dd.name == name
          case _ => false
        }
      }
    
      def detectMethodConflict(impl : Template, name : TermName) : Boolean = {
        val res = detectMethod(impl, name)
        if (res)
          error(s"name conflict, member $name is already defined")
        ! res
      }
    
      def requireParent(impl : Template, name : TypeName) : Boolean = {
        if (! impl.parents.exists(Ident(name) equalsStructure _)) {
          error(s"could not prove $name inheritance")
          false
        } else
          true
      }
    
      def checkHolder(holder : Template) : Boolean = {
        requireParent(holder, typeHolder) &
        detectMethodConflict(holder, termCompanion) &
        detectMethodConflict(holder, termNodes)
      }
    
      def checkCompanion(comp : Template) : Boolean = {
        requireParent(comp, typeCompanion) &
        detectMethodConflict(comp, termRibbon)
      }
    
      def detectNode(tree : Tree) : Boolean = tree match {
        case Apply( Select( New(Ident(tp)), termNames.CONSTRUCTOR ), _ ) => tp == typeNode
        case Apply( Select( New(AppliedTypeTree(Ident(tp), _)), termNames.CONSTRUCTOR ), _ ) => tp == typeNode
        case _ => false
      }
    
      def unwindBlock(expr : Tree) : Tree = expr match {
        case Block(_, last) => unwindBlock(last)
        case other => other
      }
    
      def extractNode(tree : Tree) : Option[TermName] = tree match {
        case ValDef(_, name, _, rhs) if detectNode(unwindBlock(rhs)) => Some(name)
        case _ => None
      }
    
      def process(source : Array[Int]) : Unit = { //side effecting on the source
        val len =  source.length - 1
        for (i <- 0 until len)
          source(i) = source(i+1)
        source(len) = 0
      }
    
      def substituteBody(classDef : ClassDef, body : Seq[Tree]) : ClassDef = {
        val impl = classDef.impl
        ClassDef( classDef.mods, classDef.name, classDef.tparams,
          Template(impl.parents, impl.self, body.toList) )
      }
      def substituteBody(moduleDef : ModuleDef, body : Seq[Tree]) : ModuleDef = {
        val impl = moduleDef.impl
        ModuleDef( moduleDef.mods, moduleDef.name,
          Template(impl.parents, impl.self, body.toList) )
      }
    
      def fillHolder(annottees : c.Expr[Any]*) : c.Expr[Any] = {
        val (holder, comp) = extractHolder.lift( annottees.map(_.tree).toList ).getOrElse(return escape)
        if (! (checkHolder(holder.impl) & checkCompanion(comp.impl)) )
          return escape
    
        val bodyHolder = holder.impl.body.to[Buffer]
        val bodyComp = comp.impl.body.to[Buffer]
    
        val nodes = holder.impl.body.flatMap(extractNode _)
        val compName = comp.name
        val ribbon = Range(0, nodes.length).toArray
        process(ribbon)
    
        bodyHolder += q"override def $termCompanion = $compName"
        bodyHolder += q"override protected val $termNodes : Array[$typeAnyNode] = new Array(${nodes.length})"
        bodyHolder ++= nodes.view.zipWithIndex.map{
          case (name, index) => q"$termPlaceNode($name, $index)"
        }
        bodyComp += q"override val $termRibbon = $ribbon"
    
        val genHolder = substituteBody(holder, bodyHolder)
        val genComp = substituteBody(comp, bodyComp)
    
        c.Expr[Any]( Block(genHolder :: genComp :: Nil, Literal(Constant(()))) )
      }
    
    }
    object Implementation {
      def impl(c : Context) : Implementation[c.type] = new Implementation[c.type](c)
      def apply(c : Context) : Implementation[c.type] = new Implementation[c.type](c)
    
      def fillHolder(c : Context)(annottees : c.Expr[Any]*) : c.Expr[Any] = impl(c).fillHolder(annottees: _*)
    }
    

    原理

    有时您想声明一个成员构成重要结构的类。每次实例化类时都会重现该结构。将此结构重定位到单例并将实例成员链接到它是很好的。可能有很多原因:计算结构的成本高,内存占用空间大,操作成本高。

    在这个简化的示例中,关系是节点之间的链连接。它是伴侣对象的因素。所有节点都可以获得链中的下一个节点。为此,他们使用宏生成表将具体实例绑定到共享结构,对协同进行间接查询。

    真实示例javafx css property。要为窗口小部件定义css属性,您应该单独定义css元数据(最好是静态),作为窗口小部件类成员的css属性并将它们链接在一起。

    在编译期间生成这样的单例非常方便。该程序将变得不那么冗长,并且可能代价高昂(假设Implementation.process具有O(n ^ 3)复杂度)重新定位到编译时。

    问题

    我转发词汇名称来确定课程。可以通过适当的导入轻松地重命名它们。 requireParentdetectMethodConflict会产生意想不到的结果。如果我可以访问语义对应物会好得多。但我不知道如何。

    将继承与Holder一起使用也很好,但我的宏实现无法处理节点定义在父节点之间分配的情况。我应该遍历一个类的所有值定义,包括在父类中定义的那些,并为它们构建通用结构。但我只能访问父母的名字,而不是它的反映。所以我无法提取他们的身体并查找它们。

0 个答案:

没有答案