@tailrec 优化和@Advice.OnMethodEnter 行为

@tailrec optimisation and @Advice.OnMethodEnter behaviour

我想检测一个用 @tailrec 注释的 Scala 方法。这导致编译器将递归方法优化为循环,只要是尾递归就可以了。

例如:

  @tailrec
  private def tailRecFunction(i: Int): Unit = {
    if (i > 0) {
      println(s"$i on Thread ${Thread.currentThread().getId}")
      Thread.sleep(20)
      tailRecFunction(i - 1)
    } else {
      println(s"0 on Thread ${Thread.currentThread().getId}")
      Thread.sleep(20)
      ()
    }
  }

我有如下建议的方法:

        @Override
        public ElementMatcher<? super MethodDescription> getMethodMatcher() {
            return named("tailRecFunction");
        }

        @Advice.OnMethodEnter(suppress = Throwable.class, inline = false)
        public static void onEnter() {
            logger.info("onMethodEnter");
        }

        @Advice.OnMethodExit(suppress = Throwable.class, inline = false)
        public static void onExit() {
            logger.info("onMethodExit");
        }

这会导致 tailRecFunction(10) 的以下日志输出:

2021-08-01 22:28:03,364 [main] INFO onMethodEnter
10 on Thread 1
9 on Thread 1
8 on Thread 1
7 on Thread 1
6 on Thread 1
5 on Thread 1
4 on Thread 1
3 on Thread 1
2 on Thread 1
1 on Thread 1
0 on Thread 1
2021-08-01 22:28:03,633 [main] INFO onMethodExit

是否有任何方法可以让我检测此方法并在每次方法调用时实际获得 onMethodEnteronMethodExit 调用,而不是一次进入和退出?

因为尾调用消除意味着,在字节码层面,方法只有一次入口和出口(在执行方面),对于像Byte Buddy这样工作在字节码层面的工具,只能见过一个 entry/exit.

请注意,Byte Buddy 是为 Java 设计的:它只真正理解类似于 Java 编译器发出的字节码。如果 Byte Buddy 可以检测循环的进入和退出,它可能能够在 Scala 中使用尾递归方法。

据我所知,在 Scala 中没有注释或编译器选项可以禁用尾调用消除,因此使用 Byte Buddy 无法实现这一点。假设您正在尝试扩充您拥有的代码,则可以通过宏检测每次迭代。