Scala 2 将方法附加到 Class 主体(元编程)
Scala 2 Append A Method To Class Body (Metaprogramming)
我已经在这个问题上停留了一个星期了,似乎没有任何进展。我正在尝试将一些方法和字段从一个 class 复制到另一个
我有两个阶段参与其中。第一阶段扫描代码,找到需要复制的方法defs,保存对应的Tree
第二阶段将这棵树插入到需要去的地方。为了简化这个问题,让我们忘记复制并说我正在尝试将一个简单的方法 def hello(): String = "hello"
插入到一些 class
的正文中
插件在 typer
之后运行(因为我需要包信息),我在正确注入类型信息时遇到了问题。这导致在后面的 type checking
阶段出现断言异常(底部的完整堆栈跟踪)
我在 metaprogramming
discord 中询问了这个问题,并被指向以下资源。
Scala compiler plugin to rewrite method calls
https://contributors.scala-lang.org/t/scala-compiler-plugin-naming-issues-after-typer/2835
但不幸的是,两者都没有取得成功。我假设我必须特别小心,因为 return 类型是原始类型 (?),因为该类型通过 Predef
连接
第一次尝试:
最后导致错误
tree match {
case pl @ ClassDef(mods, name, tparams, e @ Template(parent, self, body)) =>
parent.lift(1) match {
case Some(a @ TypeTree()) =>
a.original match {
case AppliedTypeTree(Select(This(TypeName(s)), tpt), args) =>
if (tpt.toString == "Policy") {
val insert = q""" def q(): String = {"hello"}""".asInstanceOf[DefDef]
val DefDef(dmodifiers, dname, dtparams, dvparams, dtpt, drhs) = insert
val source = treeCopy.DefDef(insert, dmodifiers, dname, dtparams, dvparams, dtpt, drhs)
val finalCopy = pl.copy(
mods,
name,
tparams,
Template(
parent,
self,
body.:+(
source
)
)
)
localTyper.typed(finalCopy)
} else {
super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
除了构建 source
,我还尝试手动构建各种东西。
DefDef(
Modifiers(),
TermName("q"),
List(),
List(List()),
TypeTree().setOriginal(Select(Select(Ident(scala), scala.Predef), TypeName("String"))), //attempt1
Ident(TypeName("String")), //attemp2
TypeTree().setOriginal(Ident(TypeName("String"))), //attempt3
gen.mkAttributedRef(typeOf[String].typeSymbol), //attempt 4
Literal(Constant("hello")))
全部导致相同的错误。请注意,在错误中,正在打印的 class 插入了方法,但类型检查器由于某种原因无法理解它
根据贡献者论坛的建议,我尝试设置所有权
val source = ... same as above
pl.symbol.owner.info.decls.unlink(pl.symbol)
localTyper.namer.enterDefDef(source)
source.symbol.owner.info.decls.enter(pl.symbol)
val finalCopy = pl.copy(....) //same as above
localTyper.namer.enterClassDef(finalCopy)
finalCopy.symbol.owner.info.decls.enter(finalCopy.symbol)
localTyper.typed(finalCopy)
但这完全搞砸了一切,编译器弄乱了符号并告诉我完全实现的 classes 没有实现抽象成员,因此需要声明 abstract
我一直在兜圈子,所以如果有人知道在 typer
之后将方法附加到 class 正文的最佳方法或有一些相关的示例,我会当然很感激
Exception in thread "main" java.lang.AssertionError: assertion failed:
[error] class UserPolicy extends AnyRef with prv.Main.Policy[prv.Main.User] {
[error] <paramaccessor> private[this] val u: prv.Main.User = _;
[error] def <init>(u: prv.Main.User): prv.Main.UserPolicy = {
[error] UserPolicy.super.<init>();
[error] ()
[error] };
[error] private[this] val data: prv.Main.User = UserPolicy.this.u;
[error] <stable> <accessor> def data: prv.Main.User = UserPolicy.this.data;
[error] protected def checkDeclassify(): prv.Main.User = {
[error] def checkExpanded(): prv.Main.User = UserPolicy.this.data;
[error] checkExpanded()
[error] };
[error] def unsafeUnwrap(reason: String): prv.Main.User = UserPolicy.this.data;
[error] def q2(): String = "hello";
[error] def q(): String = "hello"
[error] }
[error] while compiling: <test>
[error] during phase: method-wiring-phase
[error] library version: version 2.13.1
[error] compiler version: version 2.13.1
[error] reconstructed args: -usejavacp
[error] last tree to typer: type UserPolicy
[error] tree position: <unknown>
[error] symbol: <none>
[error] symbol definition: <none> (a NoSymbol)
[error] symbol package: <none>
[error] symbol owners:
[error] call site: <none> in <none>
[error] == Source file context for tree position ==
[error] at scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:170)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1876)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5794)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5886)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5948)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:254)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:195)
[error] at scala.reflect.api.Trees$Transformer.$anonfun$transformStats(Trees.scala:2614)
[error] at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2612)
[error] at scala.reflect.internal.Trees$Template.transform(Trees.scala:517)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform(TypingTransformers.scala:47)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:32)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:333)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:195)
[error] at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2587)
[error] at scala.reflect.internal.Trees$ModuleDef.$anonfun$transform(Trees.scala:370)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:32)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:24)
[error] at scala.reflect.internal.Trees$ModuleDef.transform(Trees.scala:369)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:51)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:333)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:195)
[error] at scala.reflect.api.Trees$Transformer.$anonfun$transformStats(Trees.scala:2614)
[error] at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2612)
[error] at scala.reflect.internal.Trees$PackageDef.$anonfun$transform(Trees.scala:316)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:32)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:24)
[error] at scala.reflect.internal.Trees$PackageDef.transform(Trees.scala:316)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform(TypingTransformers.scala:49)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:32)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:333)
[error] at privacy.MethodWiring$$anon.apply(MethodWire.scala:192)
[error] at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:452)
[error] at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:397)
[error] at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1506)
[error] at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1490)
[error] at scala.tools.nsc.Global$Run.compileSources(Global.scala:1482)
[error] at privacy.AnnotationFinderTest$.delayedEndpoint$privacy$AnnotationFinderTest(Test.scala:114)
[error] at privacy.AnnotationFinderTest$delayedInit$body.apply(Test.scala:13)
[error] at scala.Function0.apply$mcV$sp(Function0.scala:39)
[error] at scala.Function0.apply$mcV$sp$(Function0.scala:39)
[error] at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
[error] at scala.App.$anonfun$main(App.scala:73)
[error] at scala.App.$anonfun$main$adapted(App.scala:73)
[error] at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:553)
[error] at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:551)
[error] at scala.collection.AbstractIterable.foreach(Iterable.scala:921)
[error] at scala.App.main(App.scala:73)
[error] at scala.App.main$(App.scala:71)
[error] at privacy.AnnotationFinderTest$.main(Test.scala:13)
[error] at privacy.AnnotationFinderTest.main(Test.scala)
发布答案以便关闭问题。我花了一段时间,但我想我明白了。
感谢@SethTisue 将我指向 TwoTails
。我能够使用该回购中的部分代码正确合成一个方法。然而,最重要的是做这样的事情 after 打字机是不可能的。原因如下:
假设您正在尝试合成方法 m
并将其附加到打字机之后的 class C
。问题是如果你正在合成这个方法,你最终会在某个地方调用它 new C().m
。成员资格是在打字机期间解决的,因此打字机永远不会完成并抛出错误 method m is not a member of C
。所以,
如果你不需要包信息来实现这个,你应该在解析器之后这样做
如果您需要包信息,这会变得非常棘手。您需要在解析器之后添加一些新阶段。我将省略代码,因为它很长,但这是它的要点。
第 1 阶段: 积累 Class 您将要附加的名称及其现有方法的列表,如果是已知的请跳过class
第 2 阶段: 遍历代码并累积对应于此 class 实例的所有符号的列表。 ValDef
和 DefDef
的任何参数。如果您实施了 Hindley Milner,您将立即发现问题,这将是一种区分不同范围内相似命名符号的方法。有很多关于这方面的现有文献,您可以阅读,我跳过细节。
阶段 3: 遍历代码并积累在 C
上调用但尚不存在的方法名称列表。您还需要记住参数及其类型。你是否需要 return 类型实际上取决于你在做什么 and/or 如果你想在后面的步骤中额外 soundness/verification 。如果您要追加的方法是静态的,并且您已经提前知道将缺少哪些成员,则可以跳过此阶段。
第 4 阶段: 最后一次检查代码并将 null 方法附加到 C
中,该方法具有正确的名称和类型。重新调整 null
不是最好的选择,不确定是否有更好的选择。
稍后在打字机中将附加的方法体替换为正确的方法体(您正在复制的那个)
实际的合成看起来像这样,但正如我上面提到的,如果你真的想让它起作用,你需要弄清楚上面的所有内容。
override def transform(tree: Tree): Tree = {
val classesOfInterest = policyTypes.map(a => s"${a.packageName}.${a.typeName}").toList
tree match {
case pl @ ClassDef(mods, name, tparams, e @ Template(parent, self, body)) =>
parent.lift(1) match {
case Some(a @ TypeTree()) =>
val original = a.original
original match {
case AppliedTypeTree(Select(This(TypeName(s)), tpt), args)=>
if (tpt.toString == "Policy") {
val insert = q"""... method to insert""".asInstanceOf[DefDef]
val DefDef(dmodifiers, dname, dtparams, dvparams, dtpt, drhs) = insert
val source = treeCopy.DefDef(insert, dmodifiers, dname, dtparams, dvparams, dtpt, drhs)
//borrow the symbol of another method from the body. This is guaranteed because members like toString will be generated at this point
val xyz = mkNewMethodSymbol(body(body.length).symbol, TermName("q"))
localTyper.typedPos(tree.pos)(
treeCopy.ClassDef(
tree,
mods,
name,
tparams,
treeCopy.Template(
e,
e.parents,
e.self,
e.body :+ localTyper.typed(DefDef(xyz, mkNewMethodRhs(xyz, insert)))
)
)
)
)
} else {
super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
}
def mkNewMethodSymbol(symbol: Symbol, name: TermName): Symbol = {
val flags = METHOD
val methSym = symbol.cloneSymbol(symbol.owner, flags, name)
val param = methSym.newSyntheticValueParam(definitions.IntTpe, TermName("indx"))
methSym.modifyInfo {
case GenPolyType(tparams, MethodType(params, res)) => GenPolyType(tparams, MethodType(params, res))
}
localTyper.namer.enterInScope(methSym)
}
我已经在这个问题上停留了一个星期了,似乎没有任何进展。我正在尝试将一些方法和字段从一个 class 复制到另一个
我有两个阶段参与其中。第一阶段扫描代码,找到需要复制的方法defs,保存对应的Tree
第二阶段将这棵树插入到需要去的地方。为了简化这个问题,让我们忘记复制并说我正在尝试将一个简单的方法 def hello(): String = "hello"
插入到一些 class
插件在 typer
之后运行(因为我需要包信息),我在正确注入类型信息时遇到了问题。这导致在后面的 type checking
阶段出现断言异常(底部的完整堆栈跟踪)
我在 metaprogramming
discord 中询问了这个问题,并被指向以下资源。
Scala compiler plugin to rewrite method calls
https://contributors.scala-lang.org/t/scala-compiler-plugin-naming-issues-after-typer/2835
但不幸的是,两者都没有取得成功。我假设我必须特别小心,因为 return 类型是原始类型 (?),因为该类型通过 Predef
第一次尝试:
最后导致错误
tree match {
case pl @ ClassDef(mods, name, tparams, e @ Template(parent, self, body)) =>
parent.lift(1) match {
case Some(a @ TypeTree()) =>
a.original match {
case AppliedTypeTree(Select(This(TypeName(s)), tpt), args) =>
if (tpt.toString == "Policy") {
val insert = q""" def q(): String = {"hello"}""".asInstanceOf[DefDef]
val DefDef(dmodifiers, dname, dtparams, dvparams, dtpt, drhs) = insert
val source = treeCopy.DefDef(insert, dmodifiers, dname, dtparams, dvparams, dtpt, drhs)
val finalCopy = pl.copy(
mods,
name,
tparams,
Template(
parent,
self,
body.:+(
source
)
)
)
localTyper.typed(finalCopy)
} else {
super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
除了构建 source
,我还尝试手动构建各种东西。
DefDef(
Modifiers(),
TermName("q"),
List(),
List(List()),
TypeTree().setOriginal(Select(Select(Ident(scala), scala.Predef), TypeName("String"))), //attempt1
Ident(TypeName("String")), //attemp2
TypeTree().setOriginal(Ident(TypeName("String"))), //attempt3
gen.mkAttributedRef(typeOf[String].typeSymbol), //attempt 4
Literal(Constant("hello")))
全部导致相同的错误。请注意,在错误中,正在打印的 class 插入了方法,但类型检查器由于某种原因无法理解它
根据贡献者论坛的建议,我尝试设置所有权
val source = ... same as above
pl.symbol.owner.info.decls.unlink(pl.symbol)
localTyper.namer.enterDefDef(source)
source.symbol.owner.info.decls.enter(pl.symbol)
val finalCopy = pl.copy(....) //same as above
localTyper.namer.enterClassDef(finalCopy)
finalCopy.symbol.owner.info.decls.enter(finalCopy.symbol)
localTyper.typed(finalCopy)
但这完全搞砸了一切,编译器弄乱了符号并告诉我完全实现的 classes 没有实现抽象成员,因此需要声明 abstract
我一直在兜圈子,所以如果有人知道在 typer
之后将方法附加到 class 正文的最佳方法或有一些相关的示例,我会当然很感激
Exception in thread "main" java.lang.AssertionError: assertion failed:
[error] class UserPolicy extends AnyRef with prv.Main.Policy[prv.Main.User] {
[error] <paramaccessor> private[this] val u: prv.Main.User = _;
[error] def <init>(u: prv.Main.User): prv.Main.UserPolicy = {
[error] UserPolicy.super.<init>();
[error] ()
[error] };
[error] private[this] val data: prv.Main.User = UserPolicy.this.u;
[error] <stable> <accessor> def data: prv.Main.User = UserPolicy.this.data;
[error] protected def checkDeclassify(): prv.Main.User = {
[error] def checkExpanded(): prv.Main.User = UserPolicy.this.data;
[error] checkExpanded()
[error] };
[error] def unsafeUnwrap(reason: String): prv.Main.User = UserPolicy.this.data;
[error] def q2(): String = "hello";
[error] def q(): String = "hello"
[error] }
[error] while compiling: <test>
[error] during phase: method-wiring-phase
[error] library version: version 2.13.1
[error] compiler version: version 2.13.1
[error] reconstructed args: -usejavacp
[error] last tree to typer: type UserPolicy
[error] tree position: <unknown>
[error] symbol: <none>
[error] symbol definition: <none> (a NoSymbol)
[error] symbol package: <none>
[error] symbol owners:
[error] call site: <none> in <none>
[error] == Source file context for tree position ==
[error] at scala.reflect.internal.SymbolTable.throwAssertionError(SymbolTable.scala:170)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typedClassDef(Typers.scala:1876)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5794)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5886)
[error] at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5948)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:254)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:195)
[error] at scala.reflect.api.Trees$Transformer.$anonfun$transformStats(Trees.scala:2614)
[error] at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2612)
[error] at scala.reflect.internal.Trees$Template.transform(Trees.scala:517)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform(TypingTransformers.scala:47)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:32)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:333)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:195)
[error] at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2587)
[error] at scala.reflect.internal.Trees$ModuleDef.$anonfun$transform(Trees.scala:370)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:32)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:24)
[error] at scala.reflect.internal.Trees$ModuleDef.transform(Trees.scala:369)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:51)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:333)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:195)
[error] at scala.reflect.api.Trees$Transformer.$anonfun$transformStats(Trees.scala:2614)
[error] at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2612)
[error] at scala.reflect.internal.Trees$PackageDef.$anonfun$transform(Trees.scala:316)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:32)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:24)
[error] at scala.reflect.internal.Trees$PackageDef.transform(Trees.scala:316)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform(TypingTransformers.scala:49)
[error] at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2625)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:37)
[error] at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:32)
[error] at privacy.MethodWiring$MethodWiringPhase.transform(MethodWire.scala:333)
[error] at privacy.MethodWiring$$anon.apply(MethodWire.scala:192)
[error] at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:452)
[error] at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:397)
[error] at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1506)
[error] at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1490)
[error] at scala.tools.nsc.Global$Run.compileSources(Global.scala:1482)
[error] at privacy.AnnotationFinderTest$.delayedEndpoint$privacy$AnnotationFinderTest(Test.scala:114)
[error] at privacy.AnnotationFinderTest$delayedInit$body.apply(Test.scala:13)
[error] at scala.Function0.apply$mcV$sp(Function0.scala:39)
[error] at scala.Function0.apply$mcV$sp$(Function0.scala:39)
[error] at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
[error] at scala.App.$anonfun$main(App.scala:73)
[error] at scala.App.$anonfun$main$adapted(App.scala:73)
[error] at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:553)
[error] at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:551)
[error] at scala.collection.AbstractIterable.foreach(Iterable.scala:921)
[error] at scala.App.main(App.scala:73)
[error] at scala.App.main$(App.scala:71)
[error] at privacy.AnnotationFinderTest$.main(Test.scala:13)
[error] at privacy.AnnotationFinderTest.main(Test.scala)
发布答案以便关闭问题。我花了一段时间,但我想我明白了。
感谢@SethTisue 将我指向 TwoTails
。我能够使用该回购中的部分代码正确合成一个方法。然而,最重要的是做这样的事情 after 打字机是不可能的。原因如下:
假设您正在尝试合成方法 m
并将其附加到打字机之后的 class C
。问题是如果你正在合成这个方法,你最终会在某个地方调用它 new C().m
。成员资格是在打字机期间解决的,因此打字机永远不会完成并抛出错误 method m is not a member of C
。所以,
如果你不需要包信息来实现这个,你应该在解析器之后这样做
如果您需要包信息,这会变得非常棘手。您需要在解析器之后添加一些新阶段。我将省略代码,因为它很长,但这是它的要点。
第 1 阶段: 积累 Class 您将要附加的名称及其现有方法的列表,如果是已知的请跳过class
第 2 阶段: 遍历代码并累积对应于此 class 实例的所有符号的列表。
ValDef
和DefDef
的任何参数。如果您实施了 Hindley Milner,您将立即发现问题,这将是一种区分不同范围内相似命名符号的方法。有很多关于这方面的现有文献,您可以阅读,我跳过细节。阶段 3: 遍历代码并积累在
C
上调用但尚不存在的方法名称列表。您还需要记住参数及其类型。你是否需要 return 类型实际上取决于你在做什么 and/or 如果你想在后面的步骤中额外 soundness/verification 。如果您要追加的方法是静态的,并且您已经提前知道将缺少哪些成员,则可以跳过此阶段。第 4 阶段: 最后一次检查代码并将 null 方法附加到
C
中,该方法具有正确的名称和类型。重新调整null
不是最好的选择,不确定是否有更好的选择。稍后在打字机中将附加的方法体替换为正确的方法体(您正在复制的那个)
实际的合成看起来像这样,但正如我上面提到的,如果你真的想让它起作用,你需要弄清楚上面的所有内容。
override def transform(tree: Tree): Tree = {
val classesOfInterest = policyTypes.map(a => s"${a.packageName}.${a.typeName}").toList
tree match {
case pl @ ClassDef(mods, name, tparams, e @ Template(parent, self, body)) =>
parent.lift(1) match {
case Some(a @ TypeTree()) =>
val original = a.original
original match {
case AppliedTypeTree(Select(This(TypeName(s)), tpt), args)=>
if (tpt.toString == "Policy") {
val insert = q"""... method to insert""".asInstanceOf[DefDef]
val DefDef(dmodifiers, dname, dtparams, dvparams, dtpt, drhs) = insert
val source = treeCopy.DefDef(insert, dmodifiers, dname, dtparams, dvparams, dtpt, drhs)
//borrow the symbol of another method from the body. This is guaranteed because members like toString will be generated at this point
val xyz = mkNewMethodSymbol(body(body.length).symbol, TermName("q"))
localTyper.typedPos(tree.pos)(
treeCopy.ClassDef(
tree,
mods,
name,
tparams,
treeCopy.Template(
e,
e.parents,
e.self,
e.body :+ localTyper.typed(DefDef(xyz, mkNewMethodRhs(xyz, insert)))
)
)
)
)
} else {
super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
case _ => super.transform(tree)
}
}
def mkNewMethodSymbol(symbol: Symbol, name: TermName): Symbol = {
val flags = METHOD
val methSym = symbol.cloneSymbol(symbol.owner, flags, name)
val param = methSym.newSyntheticValueParam(definitions.IntTpe, TermName("indx"))
methSym.modifyInfo {
case GenPolyType(tparams, MethodType(params, res)) => GenPolyType(tparams, MethodType(params, res))
}
localTyper.namer.enterInScope(methSym)
}