diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index 4e694388c..0a1c5360e 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -72,6 +72,7 @@ fn generate_micropython_bindings() { .allowlist_var("MP_BUFFER_READ") .allowlist_var("MP_BUFFER_WRITE") .allowlist_var("MP_BUFFER_RW") + .allowlist_var("mp_type_str") // dict .allowlist_type("mp_obj_dict_t") .allowlist_function("mp_obj_new_dict") diff --git a/core/embed/rust/src/micropython/buffer.rs b/core/embed/rust/src/micropython/buffer.rs index 1ad33b75f..59e702474 100644 --- a/core/embed/rust/src/micropython/buffer.rs +++ b/core/embed/rust/src/micropython/buffer.rs @@ -1,7 +1,7 @@ use core::{ convert::TryFrom, ops::{Deref, DerefMut}, - ptr, slice, + ptr, slice, str, }; use crate::{error::Error, micropython::obj::Obj}; @@ -132,6 +132,50 @@ impl AsMut<[u8]> for BufferMut { } } +/// Represents an immutable UTF-8 string stored on the MicroPython heap and +/// owned by a `str` object. +/// +/// # Safety +/// +/// In most cases, it is unsound to store `StrBuffer` values in a GC-unreachable +/// location, such as static data. It is also unsound to let the contents be +/// modified while a reference to them is being held. +#[derive(Default)] +pub struct StrBuffer(Buffer); + +impl TryFrom for StrBuffer { + type Error = Error; + + fn try_from(obj: Obj) -> Result { + if obj.is_qstr() || unsafe { ffi::mp_type_str.is_type_of(obj) } { + Ok(Self(Buffer::try_from(obj)?)) + } else { + Err(Error::TypeError) + } + } +} + +impl Deref for StrBuffer { + type Target = str; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +impl AsRef for StrBuffer { + fn as_ref(&self) -> &str { + // SAFETY: MicroPython ensures that values of type `str` are UTF-8 + unsafe { str::from_utf8_unchecked(self.0.as_ref()) } + } +} + +impl From<&'static str> for StrBuffer { + fn from(val: &'static str) -> Self { + Self(Buffer::from(val.as_bytes())) + } +} + fn get_buffer_info(obj: Obj, flags: u32) -> Result { let mut bufinfo = ffi::mp_buffer_info_t { buf: ptr::null_mut(), @@ -183,3 +227,10 @@ impl crate::trace::Trace for Buffer { self.as_ref().trace(t) } } + +#[cfg(feature = "ui_debug")] +impl crate::trace::Trace for StrBuffer { + fn trace(&self, t: &mut dyn crate::trace::Tracer) { + self.as_ref().trace(t) + } +}