diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 185f9b7a1f..e0de9d1681 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -235,6 +235,7 @@ fn generate_micropython_bindings() { .allowlist_var("mp_type_fun_builtin_var") // gc .allowlist_function("gc_alloc") + .allowlist_var("GC_ALLOC_FLAG_HAS_FINALISER") // iter .allowlist_type("mp_obj_iter_buf_t") .allowlist_function("mp_getiter") diff --git a/core/embed/rust/src/micropython/gc.rs b/core/embed/rust/src/micropython/gc.rs index ae29990eeb..e283cbd7db 100644 --- a/core/embed/rust/src/micropython/gc.rs +++ b/core/embed/rust/src/micropython/gc.rs @@ -21,8 +21,17 @@ impl Copy for Gc {} impl Gc { /// Allocate memory on the heap managed by the MicroPython garbage collector - /// and then place `v` into it. `v` will _not_ get its destructor called. - pub fn new(v: T) -> Result { + /// and then place `v` into it. + /// + /// `flags` can be an int value built out of constants in the ffi module. + /// The current MicroPython only supports GC_ALLOC_FLAG_HAS_FINALISER, which + /// will cause the __del__ method to be called when the object is + /// garbage collected. + /// + /// SAFETY: + /// Flag GC_ALLOC_FLAG_HAS_FINALISER can only be used with Python objects + /// that have a base as their first element + unsafe fn alloc(v: T, flags: u32) -> Result { let layout = Layout::for_value(&v); // TODO: Assert that `layout.align()` is the same as the GC alignment. // SAFETY: @@ -32,7 +41,7 @@ impl Gc { // or the MicroPython heap. // EXCEPTION: Returns null instead of raising. unsafe { - let raw = ffi::gc_alloc(layout.size(), 0); + let raw = ffi::gc_alloc(layout.size(), flags); if raw.is_null() { return Err(Error::AllocationFailed); } @@ -41,6 +50,30 @@ impl Gc { Ok(Self::from_raw(typed)) } } + + /// Allocate memory on the heap managed by the MicroPython garbage collector + /// and then place `v` into it. `v` will _not_ get its destructor called. + pub fn new(v: T) -> Result { + unsafe { + // SAFETY: No flag is used + Self::alloc(v, 0) + } + } + + /// Allocate memory on the heap managed by the MicroPython garbage + /// collector, place `v` into it, and register for finalisation. + /// + /// `v` will **not** get its destructor called automatically! However, if + /// `v` is a Python-style object (has a base as its first field), and + /// has a `__del__` method, it will be called when the object is garbage + /// collected. You can use this to implement custom finalisation, in + /// which you can, e.g., invoke the Drop implementation. + /// SAFETY: + /// Can only be used with Python objects that have a base as their + /// first element + pub unsafe fn new_with_custom_finaliser(v: T) -> Result { + unsafe { Self::alloc(v, ffi::GC_ALLOC_FLAG_HAS_FINALISER) } + } } impl Gc<[T]> {