byteman 可以在 lambda 上触发规则吗?

Can byteman trigger a rule on a lambda?

最新的 Byteman 文档 (4.0.16) 提到了内部 classes,但没有提到 lambda。我的规则如下:

RULE showdir
CLASS ReportService
METHOD lambda$retrieveReport[=10=]
AT ENTRY
IF TRUE
DO System.out.println("XXXXXXXX");
ENDRULE

但是它似乎永远不会触发。当我 运行 bmsubmit 没有参数时,它显示规则但没有提到触发方法。我用javap检查了方法名,是正确的。我可以触发此 class 的其他非 lambda 方法。我正在 运行在 Alpine Linux 上使用 AdoptOpenJdk 8。

Byteman 支持 lambda 吗?我是否需要执行其他操作才能触发规则?

嗯,从 javap 反编译中获取实现 lambda 主体的方法的名称是识别目标方法的巧妙技巧。我不确定为什么 Byteman 无法注入 coe。你能通过 Byteman JIRA 实例报告这个吗?我将调查并在 JIRA 上报告结果。它实际上可能使这项工作成为可能。

更新:行为已在版本 4.0.17 (BYTEMAN-416) 中更改,现在可以在 lambda

上触发规则

我能够使用带有两个 lambda 的简单 class BytemanTest 重现该问题(源代码在末尾)。

简答

Byteman 忽略 lambda,因为它们在字节码中被编译器标记为“生成的代码”。

长答案:

(至少在我的测试中)编译器在生成的字节码中将 Lambda 标记为 ACC_SYNTHETIC

来自 The Java® Virtual Machine Specification :

The ACC_SYNTHETIC flag indicates that this method was generated by a compiler and does not appear in source code, unless it is one of the methods named in §4.7.8.

以下是 javap -v -p -s -c BytemanTest.class 输出的节选:

...
private static java.lang.String lambda$doSomething(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
...
private static void lambda$main[=10=](java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
        stack=3, locals=1, args_size=1

Byteman 忽略具有标志 ACC_SYNTHETIC.[= 的方法22=]

此行为(可能)首次在此票证中引入 BYTEMAN-58 (In commit ac4cbb4f。标记测试在 *Adapter classes).

在 Byteman 的 v4.0.16 源代码中,匹配目标方法的测试在 TransformContext#matchTargetMethod 中完成,它会忽略标记为 ACC_SYNTHETIC 的方法:

if ((access & (Opcodes.ACC_NATIVE|Opcodes.ACC_ABSTRACT|Opcodes.ACC_SYNTHETIC)) != 0 ||
    !targetMethodName.equals(name) ||
    (!targetDescriptor.equals("") && !TypeHelper.equalDescriptors(targetDescriptor, desc))) {
        return false;
}

我的测试class

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class BytemanTest {
    public static void main(String[] args){
        BytemanTest bt = new BytemanTest();

        bt.doSomething(args).forEach((s) -> {
            System.out.println("Out : " + s);
        });

    }

    public List<String> doSomething(String[] args){
        return Arrays.stream(args).map( s -> s + "_test").collect(Collectors.toList());
    }
}

我的 byteman 规则:

RULE showdir
CLASS BytemanTest
METHOD lambda$main[=13=]
AT ENTRY
IF TRUE
DO System.out.println("lambda matched");
ENDRULE