Search code examples
byte-buddy

What is the difference between ByteBuddy's Implementation.Composable and Implementation.Compound?


In ByteBuddy, there are seemingly two ways of sequencing Implementations.

Using one way, you can chain them together:

// Implementation.Composable; MethodCall "is a" Implementation.Composable; "is a" Implementation
someMethodCall.andThen(someOtherMethodCall)

Using another, you can just add them as a list:

// Implementation.Compound; "is a" Implementation
new Implementation.Compound(myListOfImplementations)

In both cases you end up with a single object that is, itself, an Implementation, and so is suitable for supplying to the intercept method.

Are they intended to be essentially equal ways of doing the same thing?

I ask the question because I happen to have several Implementations (MethodCalls as it happens), and so it seemed to me I could just use the Implementation.Compound constructor that takes a list of Implementations. But I'm also familiar with the andThen() method, so I hesitated.

Anyway, when I use the Implementation.Compound form, if I add two MethodCalls to a new Implementation.Compound and then use the resulting Implementation.Compound as the argument to an intercept() call, I get a bytecode verification error at class load time, complaining about a missing stack frame:

java.lang.VerifyError: 
Expecting a stack map frame
Exception Details:
  Location:
    com/foo/GeneratedSubclassOf$com$foo$AbstractFactory$26753A95.frob(Lcom/foo/TestFrob$Thing;Ljava/util/List;)V @15: aload_1
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: 2b2c 03b9 001e 0200 c000 20b8 0024 b12b
    0000010: 2c04 b900 1e02 00c0 0020 b800 27b1     

Note the trailing four spaces suggesting to my naïve eyes that perhaps something is missing here. Or perhaps it is some other artifact of my code, but when there is exactly one Implementation supplied to a new Implementation.Compound, everything works fine. So I'm thinking that perhaps the Implementation.Compound construct is not intended for sequencing Implementations (but then what is it intended for)? Or maybe there is a bug in Implementation.Compound (I would think that would be very unlikely)?

When I use the (more cumbersome) Implementation.Composable construct with the same sub-Implementations, everything works fine.

I'm concerned that I am misinterpreting what each construct is supposed to be used for.


Solution

  • An Implementation.Compound only concatenates two instances without these instances knowing each other. If you compound two implementations that contain:

    return doSomething();
    

    You end up with illegal code:

    return doSomething();
    return doSomething();
    

    Using the andThen steps of Implementation.Composable, the first method assures that it does not return a value but clears it's stack. This way, you end up:

    doSomething();
    return doSomething();
    

    Typically, these two adjusted implementations are then combined by an Implementation.Compound, but it's important that you inform the first implementation and let it create the compound.