-
-
Notifications
You must be signed in to change notification settings - Fork 809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
@Advice.Origin will not always provide the right method when an advice is applied to a bridged method #1162
Comments
Byte Buddy uses a resolver to find the "non-bridge" method for every stack, as methods and their bridges might be declared on multiple levels of a type hierarchy. Byte Buddy cannot really override bridges the same way, as it would cause endless loops: when a bridge method override invokes its actual super method, it would virtually dispatch the next bridge value; therefore this is not possible. Also, the reflection API via I generally recommend against using such bridge emission tools. The reflection API does not understand it and it confuses other frameworks, too. The problem also seems to be rather rare. Instead, if changing a return type, add a super interface with the old signature as parameterized bound type and implement it such that javac generates a valid hierarchy. If the type is non-replaceable, I recommend adding a method with a different name. I can look into Byte Buddy's behavior in such cases and see if I can improve the default, but bridge method resolution is already rather complicated, so I would not want to implode it to cover a corner case. |
Understood. That's fine by me; I mentioned this because that seemed strange, but that doesn't affect me as I don't call the bridge methods anyway. Thanks for the explanation.
I couldn't agree more; these hacks have proven a nightmare. Unfortunately, this is code I don't have control over...
I would expect this as well, but as you can see in my reproducer, this is not what is happening. In the reproducer,
Ok, so no loops in the general case; understood. Maybe it would be possible, during bytecode generation, to detect that there are bridge methods, and in that case only we would generate a loop that selects the |
I will look into it once I have some free time. I will need to investigate how javac behaves in different scenarios and how the reflection API aligns. |
Thanks @raphw , it's very much appreciated as it makes testing our |
They are causing issues with Mockito and more specifically Byte Buddy. raphw/byte-buddy#1162
Context
While the Java language forbids two methods to have the same signature with a different return type, the JVM allows this.
Thus, the bytecode of a given class can actually contain two methods with the same name and arguments, just with a different return type.
Some libraries take advantage of this using various bytecode generators in order to change the return type of a method in a new version of the library, while still preserving backwards compatibility by keeping the old version of the same method, with the old return type.
Description of the problem
Unfortunately, when applying an
Advice
, byte-buddy doesn't seem to take into account the fact that the same method can appear multiple times with a different return type:@Advice.Origin Method origin
to inject aMethod
representing the method being "advised", byte-buddy injects a method with the same name and arguments, but not necessarily the right return type.Reproducer
See https://github.com/yrodiere/bytebuddy-playground/tree/bridges-wrong-method-passed-to-advice . Just run with
./mvnw clean test
.We use this class as the base to apply an advice:
With a processor that generates two bridge methods. The bytecode ends up looking like this:
We apply this advice:
Like this:
And byte-buddy generates the following bytecode, where:
Method
instance doesn't take care of getting the right one with the right return typeAffected projects
Most problematic is the fact that this affects Mockito when trying to mock methods that happen to also have bridges declared.
In my specific case the library declaring bridge methods is https://github.com/hub4j/github-api/, and it's using https://github.com/infradna/bridge-method-injector to generate the bridge methods. When we try to mock such a method (e.g.
GHObject#getId
) with Mockito, byte-buddy passes the wrongMethod
instance to Mockito, which selects the wrong return value for the mock, ultimately leading to anNPE
when trying to convertnull
to a primitiveint
.The text was updated successfully, but these errors were encountered: