The latest Byteman documentation (4.0.16) mentions inner classes, but doesn't mention lambdas. I have a rule looking like:
RULE showdir
CLASS ReportService
METHOD lambda$retrieveReport$0
AT ENTRY
IF TRUE
DO System.out.println("XXXXXXXX");
ENDRULE
However it never seems to trigger. When I run bmsubmit without arguments it shows the rule but does not mention a trigger method. I have checked the method name with javap, and it's correct. I can trigger on other non-lambda methods of this class. I'm running AdoptOpenJdk 8 on Alpine Linux.
Does Byteman support lambdas? Do I need to do something else to have the rule trigger?
I was able to reproduce the issue using a simple class BytemanTest
with two lambdas (source code at the end).
Byteman ignores lambdas because they are marked as 'generated code' by the compiler in the bytecode.
(At least in my tests) Lambdas are flagged, by the compiler, as ACC_SYNTHETIC in the generated bytecode.
From 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.
The following is an excerpt from the output of javap -v -p -s -c BytemanTest.class
:
...
private static java.lang.String lambda$doSomething$1(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
...
private static void lambda$main$0(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 ignores methods that have the flag ACC_SYNTHETIC.
This behaviour was (probably) first introduced in this ticket BYTEMAN-58 (In commit ac4cbb4f. The flag test was in the *Adapter
classes).
In Byteman's source code for v4.0.16, The test to match the target method is done in TransformContext#matchTargetMethod and it ignores methods flagged as 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;
}
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());
}
}
RULE showdir
CLASS BytemanTest
METHOD lambda$main$0
AT ENTRY
IF TRUE
DO System.out.println("lambda matched");
ENDRULE