Scala 2 将方法附加到类主体(元编程)

2024-03-11

我已经在这个问题上陷入了一周,但似乎没有任何进展。我正在尝试将一些方法和字段从一个类复制到另一个类。

我有两个阶段参与其中。第一阶段扫描代码,找到需要复制的方法def,并保存对应的Tree

第二阶段将这棵树插入到需要的地方。为了简化这个问题,让我们忘记复制并说我正在尝试插入一个简单的方法def hello(): String = "hello"到某个阶级的身体

该插件在之后运行typer(因为我需要包信息),并且我在正确注入类型信息时遇到问题。这会导致后面的断言异常type checking阶段(底部的完整堆栈跟踪)

我在metaprogramming不和谐并被指出以下资源。

Scala 编译器插件重写方法调用 https://stackoverflow.com/questions/26219175/scala-compiler-plugin-to-rewrite-method-calls

https://contributors.scala-lang.org/t/scala-compiler-plugin-naming-issues-after-typer/2835 https://contributors.scala-lang.org/t/scala-compiler-plugin-naming-issues-after-typer/2835

但不幸的是,两者都没有取得成功的结果。我假设我必须特别小心,因为返回类型是原始类型(?),因为该类型通过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")))

所有这些都会导致相同的错误。请注意,在错误中,正在打印的类已插入方法,但类型检查器由于某种原因无法理解它

根据贡献者论坛上的建议,我尝试设置所有权


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)

但这完全搞砸了一切,编译器弄乱了符号,并告诉我完全实现的类没有实现抽象成员,因此需要声明abstract

我一直在绕圈子,所以如果有人知道在之后将方法附加到类主体的最佳方法是什么typer或者有一些相关的例子,我当然会很感激

 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$1(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$1(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$3(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$1(Trees.scala:2614)
[error]         at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2612)
[error]         at scala.reflect.internal.Trees$PackageDef.$anonfun$transform$1(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$2(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$3.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$1(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$1(App.scala:73)
[error]         at scala.App.$anonfun$main$1$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到一个班级C在打字机之后。问题是如果你正在综合这个方法,你最终会在某个地方调用它new C().m。成员资格是在打字机期间解析的,因此打字机永远不会完成并抛出错误method m is not a member of C. So,

  1. 如果您不需要包信息来实现此目的,则应该在解析器之后执行此操作

  2. 如果您需要包信息,这会变得非常棘手。您需要在解析器之后添加一些新阶段。我将省略代码,因为它很长,但这是它的要点。

    1. Phase 1:累积您将附加到的类名称列表及其现有方法,如果它是预先已知的类,则跳过

    2. Phase 2:浏览代码并累积与此类实例对应的所有符号的列表。ValDef以及任何参数DefDef。如果您已经实现了 Hindley Milner,您将立即识别问题,这将是一种区分不同范围中类似命名的符号的方法。有很多关于这方面的现有文献可供阅读,我将跳过细节。

    3. Phase 3:浏览代码并累积调用的方法名称列表C但还不存在。您还需要记住参数及其类型。您是否需要返回类型实际上取决于您正在做什么和/或您是否需要在后续步骤中进行额外的健全性/验证。如果您要附加的方法是静态的并且您已经提前知道将缺少哪些成员,则可以跳过此阶段。

    4. Phase 4:最后一遍检查代码并附加一个 null 方法到C具有正确的名称和类型。重新调整null不是最好的,不确定是否有更好的选择。

    5. 后来在打字机上将附加的方法主体替换为正确的方法主体(您正在复制的方法主体)

实际的综合看起来像这样,但正如我上面提到的,如果你真的想让它起作用,你将需要弄清楚上面的所有内容。

    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)
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala 2 将方法附加到类主体(元编程) 的相关文章

随机推荐

  • 如何让 scalac 告诉我是否有未使用的变量?

    我刚刚注意到代码中的一个错误 我创建了一个新变量 但后来未能实际使用它 我以为 scalac 会告诉我我的新变量未被使用 但情况似乎并非如此 在进行少量谷歌搜索 手册页后 我找不到任何有关启用警告的信息 我该怎么做才能启用此类警告 这个东西
  • 使用Javascript读取外部文件

    我有一个可变长度名称的外部文本文件配置文件 txt包含以下格式的信息 Jason Red Tyrannosaurus Zack Black Mastodon Billy Blue Triceratops Trini Yellow Griff
  • 如何手动刷新升压日志?

    我正在使用 boost 1 54 0 中的 Boost Log 看看它是否适合我的应用程序 一般来说 我的缓冲没有问题 所以我不想打开 auto flush 或任何东西 但我注意到在我调用之前记录的消息fork 是重复的 我想知道是否是因为
  • 如何使用 Pyomo 制作指标函数?

    我正在寻找在 Pyomo 中创建一个简单的指示变量 假设我有一个变量 x 如果 x gt 0 则该指示函数将取值 1 否则取 0 我尝试这样做的方法如下 model ConcreteModel model A Set initialize
  • 自定义智能感知自动完成

    是否可以添加自定义智能感知来自动完成我的字符串 Example 我输入 艾达 它将弹出智能感知 gt Adapt gt Adapter 我正在使用一种新语言 需要向 Visual Studio 2015 的库添加更多功能 使用我的关键字数据
  • 免费升级为付费 Android 应用,不留两个图标

    我有一个Android应用程序 有免费和付费版本 其中免费版本有时间限制 之后它将用户重定向到市场购买付费版本 这两个版本具有不同的包名称 并作为单独的应用程序提交到市场 但是 当用户购买付费版本时 这不会取代免费版本 他们会同时安装两个版
  • 使用 Glide 预加载多个图像

    我们正在努力预加载图像到缓存内存中以便稍后加载它们 图像位于资产文件夹申请的 我们尝试过的 Glide with this load pictureUri diskCacheStrategy DiskCacheStrategy ALL Gl
  • 获取 Spring Security 中所有已登录的用户

    我想获取当前登录我的应用程序的所有用户的列表 我知道 我应该注射SessionRegistry在我的代码中调用getAllPrincipals 方法 不幸的是 我总是得到空列表 看起来SessionRegistry没有填充 我不知道如何制作
  • 那么 ASP.NET MVC 3 中是否内置了 [Email] 属性?

    The Email 属性将像在未来一样内置到 ASP NET MVC 3 中吗 那么现在可以用还是不能用呢 我想这是一个相当愚蠢的问题 但我花了一些时间谷歌搜索并没有找到任何正常的答案 电子邮件正则表达式 a z d u00A0 uD7FF
  • Fancybox 导航箭头未显示

    我是 jquery 的新手 所有动画 例如过渡和速度 都工作正常 但是 showNavArrows 和 循环 不起作用 这是代码 document ready function a single image fancybox transit
  • HTTP 状态 500 - java.lang.NoClassDefFoundError:无法初始化类 org.apache.jasper.el.E​​LContextImpl [重复]

    这个问题在这里已经有答案了 当我尝试在 myeclipse 上浏览 localhost 8080 时 出现以下错误 type Exception report message java lang NoClassDefFoundError C
  • 合并特定列上的两个大型数据框并显示进度条

    我有两个大型数据集 一个 2 6 GB 另一个 1GB 我已经设法将它们作为数据帧读取 接下来 我想要创建一个新的 DataFrame 在其中我想要将两个数据集的唯一 ID 进行匹配 并丢弃两个数据集之间没有匹配的 ID 的行 我尝试过合并
  • 如何使用 JSPM 导入带有 Typescript 声明的外部 NPM 模块

    test module 是用 Typescript 编写的外部模块 带有 index d ts 定义文件 它在 package json 中有 tsd tsc 的属性 typings dist index d ts 打字稿 定义 dist
  • Xcode gRPC-Core 错误:找不到“openssl_grpc/ssl.h”文件

    Xcode 11 2 1 Mac 10 15 2 测试版 Pod 版本 1 8 4 我试图Archive我的 Mac 应用程序在 Xcode 上 然后在我的 Xcode 中出现以下错误 我可以毫无问题地编译我的应用程序 只是无法存档 我想问
  • 在主模块中找不到库模块中的包

    我有一个从 Eclipse 导入到 Android Studio 的项目 在 Eclipse 中一切都运行良好 它包含一个主模块 Eclipse 中的项目 它使用库模块 Eclipse 中的库项目 中的包 由于迁移进展不顺利 我手动创建了一
  • package-info.java 包注释影响所有类,包括子包中的类

    我想在我的所有课程上应用注释 并且我正在尝试最好的方法来做到这一点 到目前为止 我坚持使用可以注释整个包的 package info java 文件 但不能注释子包中包含的类文件 除了在我的项目中添加 100 个仅包含单个注释的相同 pac
  • 应用什么算法来连续重新分配小内存块?

    在 C 程序中 我面临需要拥有大量内存块的事务 我需要知道是否有一种算法或最佳实践技术用于处理所有这些 malloc free 我使用数组来存储这些内存块 但在某些情况下指出数组本身已满并且重新分配数组只会更加浪费 处理这个问题的优雅方法是
  • 是否可以强制通过 Wifi 或 TMobile 网络进行网络流量?

    我想知道使用 G1 是否可以强制网络流量通过 Wifi 或通过 Cell GSM 网络 我需要强制应用程序通过塔式网络连接以获取一些登录信息 这可能吗 有人对这如何可能有任何想法吗 我正在尝试使用 Android SDK 在应用程序内完成此
  • D3 - 如何将圆包转换为椭圆包?

    如何利用D3圆形包布局以获得与此类似的图表 即使有更细长的椭圆 这种图表样式的关键应用是更容易放置标签 This is jsfiddle http jsfiddle net VividD WDCpq 8 这演示了我为其他目的制作的圆形包 但
  • Scala 2 将方法附加到类主体(元编程)

    我已经在这个问题上陷入了一周 但似乎没有任何进展 我正在尝试将一些方法和字段从一个类复制到另一个类 我有两个阶段参与其中 第一阶段扫描代码 找到需要复制的方法def 并保存对应的Tree 第二阶段将这棵树插入到需要的地方 为了简化这个问题