Skip to content

Commit

Permalink
Move uniffiCheckContractApiVersion call to `IntegrityCheckingUniffi…
Browse files Browse the repository at this point in the history
…Lib`
  • Loading branch information
Sajjon committed Dec 10, 2024
1 parent fe40d99 commit c65ed07
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 22 deletions.
2 changes: 2 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<UniffiLibChecksums>(componentName)
.also { lib: UniffiLibChecksums ->
loadIndirect<IntegrityCheckingUniffiLib>(componentName)
.also { lib: IntegrityCheckingUniffiLib ->
uniffiCheckContractApiVersion(lib)
uniffiCheckApiChecksums(lib)
}
// ... and then we load the library as `UniffiLib`
Expand All @@ -109,7 +113,8 @@ internal interface UniffiLib : Library {
// a macOS M1 machine the `loadIndirect` call takes ~50ms.
loadIndirect<UniffiLib>(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 -%}
Expand All @@ -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
Expand All @@ -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")
Expand Down
22 changes: 13 additions & 9 deletions uniffi_bindgen/src/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,44 +657,48 @@ 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<Item = FfiFunction> + '_ {
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<Item = FfiFunction> + '_ {
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<Item = FfiFunction> + '_ {
let iterator = self
.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<dyn Iterator<Item = FfiFunction> + '_>
} else {
Box::new(iterator) as Box<dyn Iterator<Item = FfiFunction> + '_>
}
}

pub fn iter_ffi_function_integrity_checks(&self) -> impl Iterator<Item = FfiFunction> + '_ {
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,
) -> impl Iterator<Item = FfiFunction> + '_ {
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
Expand Down

0 comments on commit c65ed07

Please sign in to comment.