3

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?

David Plumpton
  • 1,929
  • 23
  • 31

2 Answers2

2

Hmm, grabbing the name of the method which implements the body of the lambda out of a javap decompile is a neat trick for identifying the target method. I'm not sure why Byteman is failing to inject coe. Could you report this via the Byteman JIRA instance? I'll investigate and report the outcome on the JIRA. It may actually be possible to make this work.

  • Thanks, will try to do so. Got an unexpected error while trying to sign up to create the Jira. Will try again later. – David Plumpton Jul 30 '21 at 09:12
  • Created https://issues.redhat.com/browse/BYTEMAN-416 – David Plumpton Jul 30 '21 at 20:52
  • So by making a simple change to matchTargetMethod I was able to allow transformation for all method names starting with "lambda$", which worked nicely. – David Plumpton Aug 02 '21 at 05:29
  • Yes, that commit is clearly the culprit -- although it is from such a long time ago that I do not remember why I decided it was necessary to reject synthetic methods. I do remember posting at that time on the OpenJDK Java compiler list about the meaning of SYNTHETIC. It is used for any method introduced by the compiler and we definitely don't want to inject into some such methods (e.g. BRIDGE methods). I'll need to check the spec to see if it is safe to remove this restriction -- or maybe find a way to identify safe cases like lambdas where it can be removed. – Byteman Project Aug 02 '21 at 08:45
  • Ok, I have allowed injection into locally generated lambda methods only. I checked how javac code uses SYNTHETIC for this case and it seems to be perfectly safe to inject into them. Other synthetic methods are still hors de combat as I am not convinced it is will be safe to modify them. the fix is scheduled for Byteman 4.0.17 which will be releases in the next few weeks. – Byteman Project Aug 02 '21 at 11:19
  • Thanks to both David and Mohamed for your help in reporting and resolving this issue. – Byteman Project Aug 02 '21 at 11:20
  • Thanks so much, you guys rock! – David Plumpton Aug 02 '21 at 19:55
  • I'm glad to have been of help :) I'll try to update my answer, once 4.0.17 is out, to mention that the behavior has changed. – Mohamed AMAZIRH Aug 03 '21 at 22:16
1

Update : The behavior was changed in version 4.0.17 (BYTEMAN-416), rules can now be triggered on a lambda


I was able to reproduce the issue using a simple class BytemanTest with two lambdas (source code at the end).

Short answer

Byteman ignores lambdas because they are marked as 'generated code' by the compiler in the bytecode.

Long answer:

(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;
}

My test 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());
    }
}

My byteman rule:

RULE showdir
CLASS BytemanTest
METHOD lambda$main$0
AT ENTRY
IF TRUE
DO System.out.println("lambda matched");
ENDRULE
Mohamed AMAZIRH
  • 1,159
  • 7
  • 26