From c65ed07bc50321f5e2650873b8650e32e7da221b Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Tue, 10 Dec 2024 08:19:41 +0100 Subject: [PATCH] Move `uniffiCheckContractApiVersion` call to `IntegrityCheckingUniffiLib` --- .envrc | 2 ++ .../templates/NamespaceLibraryTemplate.kt | 31 +++++++++++-------- uniffi_bindgen/src/interface/mod.rs | 22 +++++++------ 3 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..b9e0e9903 --- /dev/null +++ b/.envrc @@ -0,0 +1,2 @@ +export JAVA_OPTS="-Xmx8g" +export CLASSPATH="$PWD/jna-5.13.0.jar:$PWD/kotlinx-coroutines-core-jvm-1.8.0-RC2.jar" \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt index a3710b4ac..3b973aac9 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt @@ -71,11 +71,14 @@ fun {{ func.name() }}( // // By splitting the otherwise huge interface into two parts // * UniffiLib -// * UniffiLibChecksums (this) -// And all checksum methods are put into `UniffiLibChecksums` +// * IntegrityCheckingUniffiLib (this) // we allow for ~2x as many methods in the UniffiLib interface. -internal interface UniffiLibChecksums : Library { -{%- call decl_kotlin_functions(ci.iter_checksum_ffi_functions()) %} +// +// The `ffi_uniffi_contract_version` method and all checksum methods are put +// into `IntegrityCheckingUniffiLib` and these methods are called only once, +// when the library is loaded. +internal interface IntegrityCheckingUniffiLib : Library { +{%- call decl_kotlin_functions(ci.iter_ffi_function_integrity_checks()) %} } // A JNA Library to expose the extern-C FFI definitions. @@ -91,14 +94,15 @@ internal interface UniffiLib : Library { // // By splitting the otherwise huge interface into two parts // * UniffiLib (this) - // * UniffiLibChecksums - // And all checksum methods are put into `UniffiLibChecksums` + // * IntegrityCheckingUniffiLib + // And all checksum methods are put into `IntegrityCheckingUniffiLib` // we allow for ~2x as many methods in the UniffiLib interface. // - // Thus we first load the library with `loadIndirect` as `UniffiLibChecksums` + // Thus we first load the library with `loadIndirect` as `IntegrityCheckingUniffiLib` // so that we can call `uniffiCheckApiChecksums`... - loadIndirect(componentName) - .also { lib: UniffiLibChecksums -> + loadIndirect(componentName) + .also { lib: IntegrityCheckingUniffiLib -> + uniffiCheckContractApiVersion(lib) uniffiCheckApiChecksums(lib) } // ... and then we load the library as `UniffiLib` @@ -109,7 +113,8 @@ internal interface UniffiLib : Library { // a macOS M1 machine the `loadIndirect` call takes ~50ms. loadIndirect(componentName) .also { lib: UniffiLib -> - uniffiCheckContractApiVersion(lib) + // No need to check the contract version and checksums, since + // we already did that with `IntegrityCheckingUniffiLib` above. {% for fn in self.initialization_fns() -%} {{ fn }}(lib) {% endfor -%} @@ -122,10 +127,10 @@ internal interface UniffiLib : Library { } {%- endif %} } - {%- call decl_kotlin_functions(ci.iter_ffi_function_definitions_excluding_checksums()) %} + {%- call decl_kotlin_functions(ci.iter_ffi_function_definitions_excluding_integrity_checks()) %} } -private fun uniffiCheckContractApiVersion(lib: UniffiLib) { +private fun uniffiCheckContractApiVersion(lib: IntegrityCheckingUniffiLib) { // Get the bindings contract version from our ComponentInterface val bindings_contract_version = {{ ci.uniffi_contract_version() }} // Get the scaffolding contract version by calling the into the dylib @@ -136,7 +141,7 @@ private fun uniffiCheckContractApiVersion(lib: UniffiLib) { } @Suppress("UNUSED_PARAMETER") -private fun uniffiCheckApiChecksums(lib: UniffiLibChecksums) { +private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { {%- for (name, expected_checksum) in ci.iter_checksums() %} if (lib.{{ name }}() != {{ expected_checksum }}.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 45c15c392..9fc0ed3bf 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -657,16 +657,16 @@ impl ComponentInterface { /// The set of FFI functions is derived automatically from the set of higher-level types /// along with the builtin FFI helper functions. pub fn iter_ffi_function_definitions(&self) -> impl Iterator + '_ { - self.iter_ffi_function_definitions_conditionally_include_checksums(true) + self.iter_ffi_function_definitions_conditionally_include_integrity_checks(true) } - pub fn iter_ffi_function_definitions_excluding_checksums( + pub fn iter_ffi_function_definitions_excluding_integrity_checks( &self, ) -> impl Iterator + '_ { - self.iter_ffi_function_definitions_conditionally_include_checksums(false) + self.iter_ffi_function_definitions_conditionally_include_integrity_checks(false) } - fn iter_ffi_function_definitions_conditionally_include_checksums( + fn iter_ffi_function_definitions_conditionally_include_integrity_checks( &self, include_checksums: bool, ) -> impl Iterator + '_ { @@ -674,18 +674,23 @@ impl ComponentInterface { .iter_user_ffi_function_definitions() .cloned() .chain(self.iter_rust_buffer_ffi_function_definitions()) - .chain(self.iter_futures_ffi_function_definitions()) - .chain([self.ffi_uniffi_contract_version()]); + .chain(self.iter_futures_ffi_function_definitions()); // Conditionally determine if the checksums should be included or not. if include_checksums { - Box::new(iterator.chain(self.iter_checksum_ffi_functions())) + Box::new(iterator.chain(self.iter_ffi_function_integrity_checks())) as Box + '_> } else { Box::new(iterator) as Box + '_> } } + pub fn iter_ffi_function_integrity_checks(&self) -> impl Iterator + '_ { + iter::empty() + .chain(self.iter_checksum_ffi_functions()) + .chain([self.ffi_uniffi_contract_version()]) + } + /// Alternate version of iter_ffi_function_definitions for languages that don't support async pub fn iter_ffi_function_definitions_non_async( &self, @@ -693,8 +698,7 @@ impl ComponentInterface { self.iter_user_ffi_function_definitions() .cloned() .chain(self.iter_rust_buffer_ffi_function_definitions()) - .chain(self.iter_checksum_ffi_functions()) - .chain([self.ffi_uniffi_contract_version()]) + .chain(self.iter_ffi_function_integrity_checks()) } /// List all FFI functions definitions for user-defined interfaces