diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 0ed9985c1..a77d05a7b 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -115,7 +115,10 @@ fn generate_micropython_bindings() { .allowlist_function("mp_hal_ticks_ms") .allowlist_function("mp_hal_delay_ms") // typ - .allowlist_var("mp_type_type"); + .allowlist_var("mp_type_type") + // module + .allowlist_type("mp_obj_module_t") + .allowlist_var("mp_type_module"); // `ffi::mp_map_t` type is not allowed to be `Clone` or `Copy` because we tie it // to the data lifetimes with the `MapRef` type, see `src/micropython/map.rs`. diff --git a/core/embed/rust/src/micropython/macros.rs b/core/embed/rust/src/micropython/macros.rs index c782ff3c2..502be40ae 100644 --- a/core/embed/rust/src/micropython/macros.rs +++ b/core/embed/rust/src/micropython/macros.rs @@ -1,3 +1,4 @@ +/// Create an object for an exported function taking 1 arg. macro_rules! obj_fn_1 { ($f:expr) => {{ #[allow(unused_unsafe)] @@ -14,6 +15,7 @@ macro_rules! obj_fn_1 { }}; } +/// Create an object for an exported function taking 2 args. macro_rules! obj_fn_2 { ($f:expr) => {{ #[allow(unused_unsafe)] @@ -30,6 +32,7 @@ macro_rules! obj_fn_2 { }}; } +/// Create an object for an exported function taking 3 args. macro_rules! obj_fn_3 { ($f:expr) => {{ #[allow(unused_unsafe)] @@ -46,6 +49,7 @@ macro_rules! obj_fn_3 { }}; } +/// Create an object for an exported function taking a variable number of args. macro_rules! obj_fn_var { ($min:expr, $max:expr, $f:expr) => {{ #[allow(unused_unsafe)] @@ -63,6 +67,7 @@ macro_rules! obj_fn_var { }}; } +/// Create an object for an exported function taking key-value args. macro_rules! obj_fn_kw { ($min:expr, $f:expr) => {{ #[allow(unused_unsafe)] @@ -111,6 +116,7 @@ macro_rules! obj_dict { }}; } +/// Compose a `Type` object definition. macro_rules! obj_type { (name: $name:expr, $(locals: $locals:expr,)? @@ -134,8 +140,8 @@ macro_rules! obj_type { $(call = Some($call_fn);)? // TODO: This is safe only if we pass in `Dict` with fixed `Map` (created by - // `Map::fixed()`), because only then will Micropython treat `locals_dict` as - // immutable, and make the mutable cast safe. + // `Map::fixed()`, usually through `obj_map!`), because only then will + // MicroPython treat `locals_dict` as immutable, and make the mutable cast safe. #[allow(unused_mut)] #[allow(unused_assignments)] let mut locals_dict = ::core::ptr::null_mut(); @@ -164,3 +170,36 @@ macro_rules! obj_type { } }}; } + +/// Construct an extmod definition. +macro_rules! obj_module { + ($($key:expr => $val:expr),*) => ({ + #[allow(unused_unsafe)] + unsafe { + use $crate::micropython::ffi; + + static DICT: ffi::mp_obj_dict_t = ffi::mp_obj_dict_t { + base: ffi::mp_obj_base_t { + /// SAFETY: Reasonable to assume the pointer stays valid. + type_: unsafe { &ffi::mp_type_dict }, + }, + map: Map::from_fixed_static(&[ + $( + Map::at($key, $val), + )* + ]) + }; + ffi::mp_obj_module_t { + base: ffi::mp_obj_base_t { + type_: &ffi::mp_type_module, + }, + // This is safe only because we are passing in a static dict with fixed `Map` + // (created by `Map::from_fixed_static()`). Only then will MicroPython treat + // `globals` as immutable, making the mutable cast safe. + globals: &DICT as *const _ as *mut _, + } + }}); + ($($key:expr => $val:expr),* ,) => ({ + obj_module!($($key => $val),*) + }); +} diff --git a/core/embed/rust/src/micropython/mod.rs b/core/embed/rust/src/micropython/mod.rs index 9dd469349..dff3f9fa3 100644 --- a/core/embed/rust/src/micropython/mod.rs +++ b/core/embed/rust/src/micropython/mod.rs @@ -10,6 +10,7 @@ pub mod gc; pub mod iter; pub mod list; pub mod map; +pub mod module; pub mod obj; pub mod qstr; pub mod runtime; diff --git a/core/embed/rust/src/micropython/module.rs b/core/embed/rust/src/micropython/module.rs new file mode 100644 index 000000000..ace31beee --- /dev/null +++ b/core/embed/rust/src/micropython/module.rs @@ -0,0 +1,6 @@ +use super::ffi; + +pub type Module = ffi::mp_obj_module_t; + +// SAFETY: We are in a single-threaded environment. +unsafe impl Sync for Module {}