.. | ||
ffi | ||
jni | ||
node | ||
shared | ||
README.md |
Overview
In order for libsignal
to be used from Java, Swift, and TypeScript, the Rust
code must be exposed, or "bridged" to those languages with compatible entry
points. That's the purpose of the crates in this directory.
Bridged function definitions
libsignal-bridge
defines functions that are callable from Java,
Swift, and TypeScript. These definitions make use of the #[bridge_fn]
and
#[bridge_io]
macros to provide the language-specific glue code that makes the
functions visible from other languages.
libsignal-bridge-macros
is a proc-macro crate that
defines the #[bridge_fn]
and #[bridge_io]
macros. Attaching one of these
macros to a function definition causes the macro to emit #[cfg]
-guarded entry
point for each supported language. These entry points, before calling the actual
annotated Rust function, perform functionality like language-specific parsing
and conversion of input arguments, panic trapping, and
spawning of tasks for async
functions. They also handle return value
conversion and language-specific error handling.
The macros attach any needed code to the bridged functions to enable language-specific mechanisms (more below) to identify and collect the entry points and make them callable.
Bridged types
The libsignal-bridge-types
crate defines traits and
impls that reduce the amount of boilerplate code required to write bridged
functions. These generally fall into three categories:
- type conversions
- runtime support
- language utilities
Type conversions
Input arguments are converted from their language-native form into Rust types
using the ArgTypeInfo
traits defined for each language in the per-language
submodule (libsignal_bridge_types::ffi
, libsignal_bridge_types::jni
, and
libsignal_bridge_types::node
). To make a type usable as an argument to a
function annotated with #[bridge_fn]
or #[bridge_io]
, the three
ArgTypeInfo
traits need to be implemented for that type, either directly or
indirectly. The type also needs to be added to the ffi_arg_type
,
jni_arg_type
, and node_arg_type
macros.
Returned types are treated similarly, except that the language-specific traits
are named ResultTypeInfo
, and the macros are ffi_result_type
and similar.
Runtime support
Some Rust features aren't exposed directly to other languages, but instead
require some runtime support. As an example, when #[bridge_fn]
is attached to
an async
function, the exposed entry point takes as an additional argument the
async runtime to be used. libsignal_bridge_types
defines traits that support
this.
Language utilities
Some languages require additional code to support bridged functions. The jni
module, for example, defines a function to be invoked when the library is loaded
that caches a class loader for use during later return type conversions. The
node
module defines a function that registers a sequence of annotated
functions with a JavaScript runtime.
Bridged function collection
To make the function entry points generated by the #[bridge_fn]
and
#[bridge_io]
macros visible to the target languages, the functions must be
collected and exposed. The mechanism for this is language-specific, but relies
on annotations attached to the entry points.
For Swift, and Java, cbindgen
is used to process the
libsignal-ffi
and libsignal-jni
crates to produce a list
of function prototypes. For Java, these prototypes are munged by the
gen_java_decl.py
and interpolated into the
Native.java.in
file to produce a self-contained
class definition. For Swift, the cbindgen
output is saved directly to a
C-style .h
file that the Swift toolchain can consume.
For TypeScript, the libsignal-node
crate is expanded and processed by
gen_ts_decl.py
and the output is interpolated into
Native.ts.d.in
. The output, however, only
declares the function signatures; to make them accessible to the JavaScript
runtime, additional machinery is used. This takes the form of #[linkme]
annotations on to the generated entry points; the linkme
crate is used to
collect all these entry points at link time for explicit registration at
runtime.