Scala 3编译器插件生成预期代码,但在运行时失败

发布于 2025-01-23 05:04:20 字数 4067 浏览 6 评论 0 原文

我正在尝试开始为Scala 3编写编译器插件。在此阶段,它主要基于 https://github.com/liufengyun/scala3-plugin-example/blob/blob/master/master/plugin/plugin/src/main/main/scala/scala/phases.scala < /a>(以及随附的YouTube视频解释其工作原理)。

到目前为止,这是一个有趣的过程,我对编译器的某些方面有些感觉。

作为第一步,我只是试图将方法主体包裹在一个块中,打印出返回的对象的任何内容,然后返回对象。

这与原始插件不同,主要是因为在每个方法中添加了单个副作用方法调用 - 这也分配了局部变量(我认为这可能是问题的原因),并且将方法主体移动到一个街区。

我在这里的叉子中尽可能地以最小的示例生产:

该插件编译良好,似乎是按预期的汇编的一部分运行,然后在运行时爆炸。我不确定这是我做错了什么(不太可能)或编译器中的错误(较小的可能性,但可能性很大!)。

有人可以阐明为什么这不起作用吗?我不知道创建新符号时应该设置什么标志,所以这是一种可能性,但是有很多东西似乎可以正常工作,所以我滚动了它。

这是我在(有趣的位)的位置:

...
 override def prepareForUnit(tree: Tree)(using ctx: Context): Context =
    //find the printLn method
    val predef = requiredModule("scala.Predef")
    printlnSym = predef.requiredMethod("println", List(defn.AnyType))
    ctx

  override def transformDefDef(tree: DefDef)(using ctx: Context): Tree =
    val sym = tree.symbol

    // ignore abstract and synthetic methods
    if tree.rhs.isEmpty|| sym.isOneOf(Synthetic | Deferred | Private | Accessor)
    then return tree
    try {
      println("\n\n\n\n")
      println("========================== tree ==========================")
      println(tree.show)

      // val body = {tree.rhs}
      val body = ValDef(
        newSymbol(
        tree.symbol, termName("body"), tree.symbol.flags, tree.rhs.tpe),
        Block(Nil, tree.rhs)
      )
      
      // println(body)
      val bodyRef  = ref(body.symbol)
      val printRes = ref(printlnSym).appliedTo(bodyRef)

      // shove it all together in a block
      val rhs1 = tpd.Block(body :: printRes :: Nil, bodyRef)

      //replace RHS with new
      val newDefDef = cpy.DefDef(tree)(rhs = rhs1)
      println("====================== transformed ======================")
      println(newDefDef.show)
      newDefDef
    } catch {
      case e =>
        println("====================== error ===========================")
        println(e)
        println(e.printStackTrace)
        tree
    }
...

用于编译器插件在使用插件的编译器插件

object Test extends App:
    def foo: String = "forty two"
    def bar(x: String): Int = x.length
    def baz(x: String, y: Int): String = x + y

    baz(foo, bar(foo))

输出的测试程序(确切地说是我想要的!我此时非常兴奋)

========================== tree ==========================
def foo: String = "forty two"
====================== transformed ======================
def foo: String = 
  {
    val body: ("forty two" : String) = 
      {
        "forty two"
      }
    println(body)
    body
  }


========================== tree ==========================
def bar(x: String): Int = x.length()
====================== transformed ======================
def bar(x: String): Int = 
  {
    val body: Int = 
      {
        x.length()
      }
    println(body)
    body
  }


========================== tree ==========================
def baz(x: String, y: Int): String = x.+(y)
====================== transformed ======================
def baz(x: String, y: Int): String = 
  {
    val body: String = 
      {
        x.+(y)
      }
    println(body)
    body
  }

在运行时输出:'((这取决于代码,都会更改运行,但总是相同的主题)

Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    testing/Test$.body$2()I @0: aload_1
  Reason:
    Type top (current frame, locals[1]) is not assignable to reference type
  Current Frame:
    bci: @0
    flags: { }
    locals: { 'testing/Test$' }
    stack: { }
  Bytecode:
    0000000: 2bb6 007d ac

    at testing.Test.main(Example.scala)

编辑:我正在使用Scala 3.1.2

I'm trying to get started with writing a compiler plugin for scala 3. At this stage, it's primarily based on https://github.com/liufengyun/scala3-plugin-example/blob/master/plugin/src/main/scala/Phases.scala (and the accompanying youtube video explaining how it works).

It's been an interesting process so far, and I'm getting a bit of a feel for some aspects of the compiler.

As a first step, I'm simply trying to wrap a method body into a block, print whatever the returned object was going to be, and then return the object.

This differs from the original plugin mainly in that there was a single side-effecting method call added to each method - this is also assigning a local variable, (which I think is probably the cause of the problem), and moving the method body into a block.

I've produced as minimal of a working example as I could in a fork here: https://github.com/robmwalsh/scala3-plugin-example

The plugin compiles fine, seems to run as part of compilation as expected, and then blows up at runtime. I'm not entirely sure if this is me doing something wrong (not unlikely) or a bug in the compiler (less likely, but a distinct possibility!).

Can anybody please shed some light on why this isn't working? I don't know what flags should be set when creating a new Symbol, so that's one possibility, but there's heaps of stuff that sorta seemed to work so I rolled with it.

Here's where I'm at (the interesting bits):

...
 override def prepareForUnit(tree: Tree)(using ctx: Context): Context =
    //find the printLn method
    val predef = requiredModule("scala.Predef")
    printlnSym = predef.requiredMethod("println", List(defn.AnyType))
    ctx

  override def transformDefDef(tree: DefDef)(using ctx: Context): Tree =
    val sym = tree.symbol

    // ignore abstract and synthetic methods
    if tree.rhs.isEmpty|| sym.isOneOf(Synthetic | Deferred | Private | Accessor)
    then return tree
    try {
      println("\n\n\n\n")
      println("========================== tree ==========================")
      println(tree.show)

      // val body = {tree.rhs}
      val body = ValDef(
        newSymbol(
        tree.symbol, termName("body"), tree.symbol.flags, tree.rhs.tpe),
        Block(Nil, tree.rhs)
      )
      
      // println(body)
      val bodyRef  = ref(body.symbol)
      val printRes = ref(printlnSym).appliedTo(bodyRef)

      // shove it all together in a block
      val rhs1 = tpd.Block(body :: printRes :: Nil, bodyRef)

      //replace RHS with new
      val newDefDef = cpy.DefDef(tree)(rhs = rhs1)
      println("====================== transformed ======================")
      println(newDefDef.show)
      newDefDef
    } catch {
      case e =>
        println("====================== error ===========================")
        println(e)
        println(e.printStackTrace)
        tree
    }
...

test program for compiler plugin

object Test extends App:
    def foo: String = "forty two"
    def bar(x: String): Int = x.length
    def baz(x: String, y: Int): String = x + y

    baz(foo, bar(foo))

output during compile using plugin (exactly what I wanted! I got very excited at this point)

========================== tree ==========================
def foo: String = "forty two"
====================== transformed ======================
def foo: String = 
  {
    val body: ("forty two" : String) = 
      {
        "forty two"
      }
    println(body)
    body
  }


========================== tree ==========================
def bar(x: String): Int = x.length()
====================== transformed ======================
def bar(x: String): Int = 
  {
    val body: Int = 
      {
        x.length()
      }
    println(body)
    body
  }


========================== tree ==========================
def baz(x: String, y: Int): String = x.+(y)
====================== transformed ======================
def baz(x: String, y: Int): String = 
  {
    val body: String = 
      {
        x.+(y)
      }
    println(body)
    body
  }

output during runtime :'( (this changes depending on the code it's running on, but always the same theme)

Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    testing/Test$.body$2()I @0: aload_1
  Reason:
    Type top (current frame, locals[1]) is not assignable to reference type
  Current Frame:
    bci: @0
    flags: { }
    locals: { 'testing/Test

Edit: I'm using scala 3.1.2

} stack: { } Bytecode: 0000000: 2bb6 007d ac at testing.Test.main(Example.scala)

Edit: I'm using scala 3.1.2

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

烂柯人 2025-01-30 05:04:20

创建新符号时,我正在使用现有标志。相反,我需要使用 local 标志,我想这很有意义。

I was using the existing flags when creating my new symbol. Instead, I needed to use the Local flag, which I suppose makes sense.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文