Skip bridge methods in contract parsing and map them for dispatch - #3517
Skip bridge methods in contract parsing and map them for dispatch#3517AzazelSensei wants to merge 3 commits into
Conversation
Covariant overrides generate unannotated bridge methods that BaseContract previously tried to parse, failing inheritance tests such as overrideParameterizedApiSupported. Fixes OpenFeign#2752
velo
left a comment
There was a problem hiding this comment.
Thanks for digging into this area, but I can't take this as-is: the change introduces a regression, and the problem it claims to fix isn't reproducible on current master.
1. DefaultContractInheritanceTest already passes on master
mvn -pl core test -Dtest=DefaultContractInheritanceTest
Tests run: 9, Failures: 0, Errors: 0, Skipped: 0
So overrideParameterizedApiSupported and multipleInheritanceDoneCorrectly2 are green without this patch. If there's still a broken case, it isn't one of those — please share the interface that actually fails.
2. Skipping bridge methods breaks dispatch through a generic super-interface
Bridge methods only cause trouble when the erased signature differs from the override. Here's a repro:
interface CrudApi<T> {
@RequestLine("GET /items/{id}")
String get(@Param("id") T id);
}
interface UserApi extends CrudApi<String> {
@Override
@RequestLine("GET /users/{id}")
String get(@Param("id") String id);
}
CrudApi<String> api = (CrudApi<String>) Feign.builder()
.client((request, options) -> Response.builder().status(200).request(request).build())
.target(UserApi.class, "http://localhost:1");
api.get("1");- master: works.
- with this PR:
java.lang.UnsupportedOperationException: Method "get" should not be called
Why: javac emits String get(Object) on UserApi as a bridge. That's a distinct erased signature, so the JDK proxy dispatches on the bridge Method object when the call comes in through CrudApi. ReflectiveFeign.ParseHandlersByName#apply only populates the dispatch map from (a) contract metadata and (b) Util.isDefault methods — and Util.isDefault explicitly excludes synthetic methods (see the comment at Util.java:124-135). Drop bridges from the contract and they lose their only dispatch entry, so FeignInvocationHandler falls through to the UnsupportedOperationException branch at ReflectiveFeign.java:99.
What a working version would need
If bridge methods should be excluded from parsing, ReflectiveFeign has to compensate — map each bridge Method onto the handler of the method it bridges to, so calls arriving through the generic super-interface still resolve. That's the substantive part of the change, and it belongs in the same PR.
Also
Please add a test that fails without the patch. Right now the diff has no test coverage at all, and the two named tests were already passing, so there's nothing demonstrating the fix works or guarding against the regression above.
Skipping bridges in BaseContract left generic super-interface calls (e.g. CrudApi<T> via UserApi) without a dispatch entry. Resolve each bridge Method to the handler of the method it bridges to, and cover that path in BridgeMethodTest.
|
@velo Thanks for the catch — updated in place on this branch. Skipping bridges in |
8d9b804 to
846f378
Compare
|
Fixed BridgeMethodTest formatting for CI. |
|
@velo Ran So the original skip was aimed at a case I can't actually show. The ReflectiveFeign mapping is only there because that skip broke the If you'd rather not take this without a failing example, I'll close it. If you still want the skip + dispatch mapping as a hardening change, I'll leave it up. |
Summary
Contract.BaseContract.parseAndValidateMetadatawalksClass.getMethods()and previously tried to parse compiler-generated bridge/synthetic methods. This PR skips those during contract parsing.Skipping bridges alone is not enough: when a call arrives through a generic super-interface (erased signature), the JDK proxy dispatches on the bridge
Method.ReflectiveFeignnow maps each bridge method onto the handler of the method it bridges to, so those calls still resolve.Changes
isSynthetic()/isBridge()methods inBaseContract.parseAndValidateMetadataReflectiveFeign.ParseHandlersByName, resolve bridge methods to their bridged targets and reuse that handler in the dispatch mapBridgeMethodTestcovering:UserApi#get(Object))CrudApi<String>calls through aUserApitarget still dispatch (the regression called out in review)Test plan
mvn test -pl core -Dtest=BridgeMethodTest,DefaultContractInheritanceTestFixes #2752