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