-
Notifications
You must be signed in to change notification settings - Fork 564
[native] Use full marshal method signature when a method is overloaded #10433
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
Merged
jonathanpeppers
merged 3 commits into
main
from
dev/grendel/marshal-methods-maui-images
Aug 21, 2025
Merged
[native] Use full marshal method signature when a method is overloaded #10433
jonathanpeppers
merged 3 commits into
main
from
dev/grendel/marshal-methods-maui-images
Aug 21, 2025
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Fixes: #10417 Context: https://docs.oracle.com/en/java/javase/17/docs/specs/jni/design.html#resolving-native-method-names Context: #10417 (comment) Whenever we generate marshal methods code for overloaded Java methods, we must be sure to use full native signature instead of the short form, even if one of the overloads doesn't have parameters. Failure to do so results in hard-to fix subtle bugs like in case of #10417. In this instance, we had a class (`Android.Runtime.InputStreamAdapter`) with 3 method overloads: * `public override int Read ();` * `public override int Read (byte[] bytes)` * `public override int Read (byte[] bytes, int offset, int length)` The sample application failed to load an image using a Glide decoder, which called the `Read (byte[], int, int)` overload above. It was puzzling, because the Java code clearly called the right native function and yet, it ended up in a marshal method wrapper for the `Read ()` overload. The cause became apparent when I checked names of the generated native functions. All of them were valid and yet the whole was subtly broken: the `Read ()` overload used a (valid) "short form" native symbol name, which omits the part that encodes parameter types in the symbol name. The reason for this was that the generator code saw there were no parameters and so it output the short form, because it is the **first** symbol name looked up by JavaVM - thus it seemed to be a good optimization at the time of implementing the marshal methods generator. The problems began when **both** overloads were used somewhere in the application code and the `Read()` overload was **always** found first, since it used the short form name, and thus Java never got to looking up the other overload! Then it proceeded to call the parameter-less overload, which caused sort-of worked (as in it didn't crash) it it was far from correct. The fix implemented here is to always emit the `__` native symbol suffix for any overloaded methods that have no parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a subtle bug in native method resolution for overloaded Java methods by ensuring marshal methods always use full native signatures when methods are overloaded, even when one overload has no parameters.
Key Changes:
- Modified
MakeNativeSymbolNameto always append the__suffix for overloaded methods, regardless of parameter presence - Added logic to distinguish between having parameters and needing the full signature format
- Ensures proper method resolution when multiple overloads exist
This comment was marked as outdated.
This comment was marked as outdated.
These tests should return 1024, but fail with MM on:
08-21 11:53:13.738 16263 16289 I NUnit : InputStreamAdapter_Read_bytes
08-21 11:53:13.738 16263 16289 D StreamTest: StreamTest.InputStreamAdapter_Read_bytes, underlying stream type: mono.android.runtime.InputStreamAdapter
08-21 11:53:13.745 16263 16289 E NUnit : [FAIL]
08-21 11:53:13.746 16263 16289 E NUnit : : Expected: 1024
08-21 11:53:13.746 16263 16289 E NUnit : But was: 0
08-21 11:53:13.746 16263 16289 E NUnit : at System.IOTests.StreamTest.InputStreamAdapter_Read_bytes()
Contributor
Author
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
jonathanpeppers
approved these changes
Aug 21, 2025
Member
jonathanpeppers
added a commit
that referenced
this pull request
Aug 21, 2025
#10433) Fixes: #10417 Context: https://docs.oracle.com/en/java/javase/17/docs/specs/jni/design.html#resolving-native-method-names Context: #10417 (comment) Whenever we generate marshal methods code for overloaded Java methods, we must be sure to use full native signature instead of the short form, even if one of the overloads doesn't have parameters. Failure to do so results in hard-to fix subtle bugs like in case of #10417. In this instance, we had a class (`Android.Runtime.InputStreamAdapter`) with 3 method overloads: * `public override int Read ();` * `public override int Read (byte[] bytes)` * `public override int Read (byte[] bytes, int offset, int length)` The sample application failed to load an image using a Glide decoder, which called the `Read (byte[], int, int)` overload above. It was puzzling, because the Java code clearly called the right native function and yet, it ended up in a marshal method wrapper for the `Read ()` overload. The cause became apparent when I checked names of the generated native functions. All of them were valid and yet the whole was subtly broken: the `Read ()` overload used a (valid) "short form" native symbol name, which omits the part that encodes parameter types in the symbol name. The reason for this was that the generator code saw there were no parameters and so it output the short form, because it is the **first** symbol name looked up by JavaVM - thus it seemed to be a good optimization at the time of implementing the marshal methods generator. The problems began when **both** overloads were used somewhere in the application code and the `Read()` overload was **always** found first, since it used the short form name, and thus Java never got to looking up the other overload! Then it proceeded to call the parameter-less overload, which caused sort-of worked (as in it didn't crash) it it was far from correct. The fix implemented here is to always emit the `__` native symbol suffix for any overloaded methods that have no parameters. * Added on-device test to repro issue. These tests should return 1024, but fail with `$(AndroidEnableMarshalMethods)`: 08-21 11:53:13.738 16263 16289 I NUnit : InputStreamAdapter_Read_bytes 08-21 11:53:13.738 16263 16289 D StreamTest: StreamTest.InputStreamAdapter_Read_bytes, underlying stream type: mono.android.runtime.InputStreamAdapter 08-21 11:53:13.745 16263 16289 E NUnit : [FAIL] 08-21 11:53:13.746 16263 16289 E NUnit : : Expected: 1024 08-21 11:53:13.746 16263 16289 E NUnit : But was: 0 08-21 11:53:13.746 16263 16289 E NUnit : at System.IOTests.StreamTest.InputStreamAdapter_Read_bytes() If `0` is returned, it means that `read()` was called instead of the `read(...)` overload which reproduces the issue. This test appears to pass with the above changes. Co-authored-by: Jonathan Peppers <[email protected]>
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.

Fixes: #10417
Context: https://docs.oracle.com/en/java/javase/17/docs/specs/jni/design.html#resolving-native-method-names
Context: #10417 (comment)
Whenever we generate marshal methods code for overloaded Java methods, we must be sure to use full native signature
instead of the short form, even if one of the overloads doesn't have parameters.
Failure to do so results in hard-to fix subtle bugs like in case of #10417. In this instance, we had a
class (
Android.Runtime.InputStreamAdapter) with 3 method overloads:public override int Read ();public override int Read (byte[] bytes)public override int Read (byte[] bytes, int offset, int length)The sample application failed to load an image using a Glide decoder, which called the
Read (byte[], int, int)overload above. It was puzzling, because the Java code clearly called the right native function and yet, it ended up in
a marshal method wrapper for the
Read ()overload.The cause became apparent when I checked names of the generated native functions. All of them were valid and yet the
whole was subtly broken: the
Read ()overload used a (valid) "short form" native symbol name, which omits the partthat encodes parameter types in the symbol name. The reason for this was that the generator code saw there were no
parameters and so it output the short form, because it is the first symbol name looked up by JavaVM - thus it seemed
to be a good optimization at the time of implementing the marshal methods generator.
The problems began when both overloads were used somewhere in the application code and the
Read()overloadwas always found first, since it used the short form name, and thus Java never got to looking up the other overload!
Then it proceeded to call the parameter-less overload, which caused sort-of worked (as in it didn't crash) it it was far
from correct.
The fix implemented here is to always emit the
__native symbol suffix for any overloaded methods that have noparameters.