-
Notifications
You must be signed in to change notification settings - Fork 238
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
MethodTooLargeException
runtime error when executing Android tests for our Kotlin artifact
#2340
Comments
I fixed the issue and then got it again, this small commit causes the exception: It add one more #[derive(Debug, uniffi::Object)]
pub struct SecurityShieldBuilder {
wrapped: RwLock<sargon::SecurityShieldBuilder>,
} impl SecurityShieldBuilder {
fn set(
&self,
mut write: impl FnMut(
&mut sargon::SecurityShieldBuilder,
) -> &sargon::SecurityShieldBuilder,
) {
let mut binding = self.wrapped.write().expect("No poison");
_ = write(&mut binding);
}
}
#[uniffi::export]
impl SecurityShieldBuilder {
pub fn set_name(&self, name: String) {
self.set(|builder| builder.set_name(&name));
}
...
} And adding one more method - #[uniffi::export]
impl SecurityShieldBuilder {
pub fn set_name(&self, name: String) {
self.set(|builder| builder.set_name(&name));
}
...
+ pub fn reset_recovery_and_confirmation_role_state(&self) { // <-- ❌ addition of this method => `MethodTooLargeException`
+ self.set(|builder| builder.reset_recovery_and_confirmation_role_state());
+ }
} |
Update, we think we know the issue, we believe the issue to be that UniFFI generates a too big @Suppress("UNUSED_PARAMETER")
private fun uniffiCheckApiChecksums(lib: UniffiLib) { method. Ours is 3484 lines, 251kb, which seems to be too large for I'm gonna try to manually split it and re-run our Kotlin JVM tests and see if it works. |
(Sorry for switching between private and work Github handlers. Im switching between work computer and personal sometimes) Confirmed that it is both the I first started by splitting the huge Then I also commented out four methods in the So I tried reverting the Now ofc I do need those four commented out methods... so hmm so maybe I can "hack around" the limitation on number of methods on an interface? I'm gonna try: interface UniffiLibPart1: Library {
// half of all methods go here
}
interface UniffiLibPart2: UniffiLibPart1 {
// second half of all methods go here
}
interface UniffiLib: UniffiLibPart2 {} |
I shall mentioned that we are still on UniFFI uniffi = { git = "https://github.com/mozilla/uniffi-rs/", rev = "6f33088e8100a2ea9586c8c3ecf98ab51d5aba62", features = [
"cli",
] } I might try to bump to latest and see if anything has changed. |
I guess that makes sense to me and I don't have any other ideas for solving it. It seems fairly lame of JDK though :) I doubt this has changed since 0.27 |
I think it is unlikely that Sargon is the biggest UniFFI using project in the world? Sure it is a pretty big project - but surely some Mozilla projects must be bigger. So I guess Sargon is the biggest UniFFI using crate, and that is why we are first with hitting this JDK issue - and Mozilla (or any other) is not hit (yet) since Mozilla uses multi-crate setup? I might try yet again the multi-crate setup now that we have @bendk s uniffi-bindgen swift change. But I would like to understand if I can try some other JDK version to see if it works? Or hmm, Im really out of my depth here with this JDK stuff... I guess I need to know if UniFFI imposes some JDK version? Or if I can try upgrading locally on my machine and UniFFI will use/respect that? Or is that up to Because OpenJDK fixed this semi-recently (1 year ago, which maybe counts as recently in the Java world) |
Exactly - we have many small(-ish) crates. Re the jdk version, uniffi doesn't have any opinion there. I'm not sure what "cargo ndk" is, but we don't use it IIUC. Updating to a new jdk should be fine - it looks like we are currently on jdk 17 (which seems early, so I'm not 100% sure what we actually release with, but that's what I seem to have locally) |
@mhammond Good news, I've fixed the issue - by manually edit the huge (generated) So I will soon create a PR, let me know if you want me to gate it behind a UniFFI build flag Here is what I did to get it to work - let me know if anyone has any better idea: I've split the internal interface UniffiLib : Library {}
internal interface UniffiLibBatch2: Library { ... } I've moved out some methods that I know to be called from our JVM tests from This is the complete change: + internal interface UniffiLibBatch2: Library {
+ companion object {
+ internal val INSTANCE: UniffiLibBatch2 by lazy {
+ loadIndirect<UniffiLibBatch2>(componentName = "sargon")
+ .also { lib: UniffiLibBatch2 ->
+ uniffiCheckContractApiVersionBatch2(lib)
+ uniffiCheckApiChecksumsBatch2(lib)
+ }
+ }
+
+ // The Cleaner for the whole library
+ internal val CLEANER: UniffiCleaner by lazy {
+ UniffiCleaner.create()
+ }
+ }
+
+ fun uniffi_sargon_uniffi_fn_func_new_foobar1(uniffi_out_err: UniffiRustCallStatus): RustBuffer.ByValue
+ fun uniffi_sargon_uniffi_fn_func_new_foobar2(uniffi_out_err: UniffiRustCallStatus): RustBuffer.ByValue
+
+ fun uniffi_sargon_uniffi_checksum_func_new_foobar1(): Short
+ fun uniffi_sargon_uniffi_checksum_func_new_foobar2(): Short
+
+ }
+
+@Suppress("UNUSED_PARAMETER")
+private fun uniffiCheckApiChecksumsBatch2(lib: UniffiLibBatch2) {
+ if (lib.uniffi_sargon_uniffi_checksum_func_new_foobar1() != 21850.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+ if (lib.uniffi_sargon_uniffi_fn_func_new_foobar2() != 33734.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
+}
@Suppress("UNUSED_PARAMETER")
private fun uniffiCheckApiChecksums(lib: UniffiLib) {
if (lib.uniffi_sargon_uniffi_checksum_func_frobnicate0001() != 16739.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
/* Many THOUSANDS of lines omitted */
- if (lib.uniffi_sargon_uniffi_checksum_func_new_foobar1() != 21850.toShort()) {
- throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
- }
- if (lib.uniffi_sargon_uniffi_fn_func_new_foobar2() != 33734.toShort()) {
- throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
- }
if (lib.uniffi_sargon_uniffi_checksum_func_frobnicate1337() != 16739.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
}
internal interface UniffiLib: Library {
companion object {
/* UNCHANGED */
}
fun uniffi_sargon_uniffi_fn_func_frobnicate0001(uniffi_out_err: UniffiRustCallStatus): RustBuffer.ByValue
/* Thousands of lines omitted */
fun uniffi_sargon_uniffi_fn_func_frobnicate1337(uniffi_out_err: UniffiRustCallStatus): RustBuffer.ByValue
- fun uniffi_sargon_uniffi_fn_func_new_foobar1(uniffi_out_err: UniffiRustCallStatus): RustBuffer.ByValue
- fun uniffi_sargon_uniffi_fn_func_new_foobar2(uniffi_out_err: UniffiRustCallStatus): RustBuffer.ByValue
fun uniffi_sargon_uniffi_checksum_func_frobnicate0001(): Short
/* Thousands of lines omitted */
fun uniffi_sargon_uniffi_checksum_func_frobnicate1337(): Short
- fun uniffi_sargon_uniffi_checksum_func_new_foobar1(): Short
- fun uniffi_sargon_uniffi_checksum_func_new_foobar2(): Short
}
/* Thousands of lines omitted */
fun `newFoobar1`(): Foobar {
return FfiConverterTypeFoobar.lift(
uniffiRustCall { _status ->
- UniffiLib.INSTANCE.uniffi_sargon_uniffi_fn_func_new_foobar1(_status)
+ UniffiLibBatch2.INSTANCE.uniffi_sargon_uniffi_fn_func_new_foobar1(_status)
},
)
}
fun `newFoobar2`(): Foobar {
return FfiConverterTypeFoobar.lift(
uniffiRustCall { _status ->
- UniffiLib.INSTANCE.uniffi_sargon_uniffi_fn_func_new_foobar2(_status)
+ UniffiLibBatch2.INSTANCE.uniffi_sargon_uniffi_fn_func_new_foobar2(_status)
},
)
} So it is a matter of coming up with good thresholds for when the |
I suspect the devil will be in the detail, but if the PR makes uniffi work where it otherwise would not, then there would be no need for a feature flag.
eg, this is where the devil will live :) I suspect that the number of methods might indicate that "maybe there's a problem around here, although some objects with many fewer might trigger it, and some objects with many more might be fine" - which probably wouldn't be an actual fix. Maybe the other solution is "don't do that - just use multiple crates"? |
…hecksum fn from UniffiLib interface into separate one.
…hecksum fn from UniffiLib interface into separate one.
…hecksum fn from UniffiLib interface into separate one.
…_fn_into_seperate_interface Fix JNA bug `MethodTooLargeException` mozilla#2340 by splitting out checksum…
Fixed in #2344 |
…hecksum fn from UniffiLib interface into separate one.
I recently started getting
MethodTooLargeException
errors, e.g.I've never had this issue before. I got it when working on a fairly large addition to our UniFFI layer in this PR
I've added some
#[derive(uniffi::Object)]
builders which contain someRwLock
state.I'm unable to understand why I got hit with this, if it is because the size of our whole Kotlin artifact or if it relates to implementation (disregarding other code).
The mention
com.radixdlt.sargon.samples.AccessControllerAddressSampleMainnet
mentioned packages in the stack trace is a red herring, those types have not changed! Which makes me thing that we get hit by this because of size of or artifact?I'm surprised first of all that no one else have had this issue? Or at least if I search for it in issues here on GH there are no results.
Any help whatsoever what be greatly appreciated!
The text was updated successfully, but these errors were encountered: