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

Scala 如何获取宏定义中提到的类的反射数据?,scala,reflection,macros,Scala,Reflection,Macros,我正在使用白盒宏注释来生成代码。我希望超越琐碎的准整数替换并根据额外信息决定宏: 检查术语的实际类型 查找特征的所有成员,包括继承的 据我所知,在调用宏扩展时,当前处理的文件还不能用于反射。它仍然是AST形式 但是,到那时,所有依赖文件都应该已经编译好了。因此,如果传递给宏定义的AST具有名为MyType的类型,则该类型应该已经在某处声明。不幸的是,MyType可能是import语句中引入的别名 因此,我认为制作富宏需要解决以下问题: 从给定的术语计算其规范名称 检查当前文件中是否定义了该名

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

  • 检查术语的实际类型
  • 查找特征的所有成员,包括继承的
据我所知,在调用宏扩展时,当前处理的文件还不能用于反射。它仍然是AST形式

但是,到那时,所有依赖文件都应该已经编译好了。因此,如果传递给宏定义的AST具有名为
MyType
的类型,则该类型应该已经在某处声明。不幸的是,
MyType
可能是import语句中引入的别名

因此,我认为制作富宏需要解决以下问题:

  • 从给定的术语计算其规范名称
  • 检查当前文件中是否定义了该名称,并获取相应的符号树
  • 在另一种情况下,获取包含已编译文件的项目类路径,并在其中搜索规范名称
  • 而且,我不知道如何完成每项任务。你能建议一条出去的路吗


    代码示例 相当长的代码示例,但仍然很少。编写宏相当冗长

    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: _*)
    }
    
    导入scala.language.experimental.macros
    导入scala.reflect.macros.Context
    导入scala.annotation.StaticAnnotation
    仅导入scala.annotation.compileTimeOnly
    导入scala.collection.mutable.Buffer
    性状伴侣{
    val功能区:数组[Int]
    }
    特质持有者{
    密封特征任意节点{
    私有[持有人]var_指数:Int=0
    定义索引:Int=\u索引
    //伴侣的示例使用
    def next:AnyNode=
    节点(伴生功能区(_索引))
    }
    类节点[T](默认值:T)扩展任何节点{
    私有var_值:T=默认值
    定义值:T=_值
    def值=(nv:T)={
    _值=nv
    }
    重写def toString:String=s“节点(${this.\u value})”
    }
    def companion:companion//宏中的覆盖
    受保护的val节点:数组[AnyNode]//在宏中重写
    //生成子序列的访问器
    受保护的def placeNode(节点:AnyNode,位置:Int){
    节点。_索引=位置
    节点(位置)=节点
    }
    //调试输出
    def printNodes:String=“nodes:+nodes.mkString(;”)
    }
    @compileTimeOnly(“启用宏天堂以展开宏注释”)
    类fillHolder扩展了StaticAnnotation{
    def macroTransform(annottees:Any*):Any=macro Implementation.fillHolder
    }
    类实现[C(holder,makeCompanion(holder.name))
    案例(持有人:ClassDef):(公司:ModuleDef)::无=>(持有人,公司)
    }
    def检测方法(impl:Template,name:TermName):布尔={
    impl.body.exists{
    案例vd:ValDef=>vd.name==name
    案例dd:DefDef=>dd.name==name
    大小写=>false
    }
    }
    def detectMethodConflict(impl:Template,name:TermName):布尔={
    val res=检测方法(impl,name)
    如果(res)
    错误(s“名称冲突,成员$name已定义”)
    !res
    }
    def requireParent(impl:Template,name:TypeName):布尔={
    如果(!impl.parents.exists(Ident(name)equalstructure )){
    错误“无法证明$name继承”)
    错误的
    }否则
    符合事实的
    }
    def支票持有人(持有人:模板):布尔={
    所需租金(固定器、打字机固定器)&
    检测方法冲突(保持架、术语配对)&
    检测方法冲突(保持架、终端节点)
    }
    def checkCompanion(组件:模板):布尔={
    所需租金(公司、打字机)&
    检测方法冲突(组件、术语功能区)
    }
    def detectNode(树:树):布尔=树匹配{
    案例应用(选择(新的(标识(tp)),termNames.CONSTRUCTOR),)=>tp==typeNode
    case Apply(选择(新建(AppliedTypeTree(Ident(tp),)),termNames.CONSTRUCTOR),)=>tp==typeNode
    大小写=>false
    }
    def unwindBlock(expr:Tree):Tree=expr匹配{
    案例块(u,last)=>展开块(last)
    案例其他=>其他
    }
    def extractNode(树:树):选项[TermName]=树匹配{
    如果detectNode(unwindBlock(rhs))=>Some(name),则大小写ValDef(u,name,,rhs)
    案例=>无
    }
    def进程(source:Array[Int]):Unit={//对源的副作用
    val len=source.length-1
    对于(i q“$termPlaceNode($name,$index)”
    }
    bodyComp+=q“覆盖值$termRibbon=$ribbon”
    val genHolder=替代底座(支架,车身支架)
    val genComp=替代主体(主体、主体)
    c、 Expr[Any](块(genHolder::genComp::Nil,Literal(常量(()))
    }
    }
    对象实现{
    def impl(c:Context):实现[c.type]=新实现[c.type](c)
    def apply(c:Context):实现[c.type]=新实现[c.type](c)
    def fillHolder(c:Context)(注释项:c.Expr[Any]*):c.Expr[Any]=impl(c).fillHolder(注释项:*)
    }
    
    根本原因 有时,您希望声明一个成员构成重要结构的类。每次实例化该类时都会复制该结构。最好将此结构重新定位到一个单例并将实例成员链接到它。这可能有很多原因:计算该结构的成本太高,内存占用太大这意味着,成本高昂的操作

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

    现实世界的例子是。要定义小部件的css属性,您应该单独定义css元数据(最好是静态的),css属性是小部件类的成员,并将它们链接在一起

    在编译时生成这样的单例将非常方便。程序将变得不那么冗长,并且可能成本高昂