From 4f9b2ef3b2202513efb47cc145676cc6d305610d Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Mon, 13 Jul 2026 13:27:31 +0200 Subject: [PATCH 1/2] Add codecs support to c-apo --- crates/capi/src/codecs.rs | 308 ++++++++++++++++++++++++++++++++++++++ crates/capi/src/lib.rs | 1 + 2 files changed, 309 insertions(+) create mode 100644 crates/capi/src/codecs.rs diff --git a/crates/capi/src/codecs.rs b/crates/capi/src/codecs.rs new file mode 100644 index 00000000000..390e6108752 --- /dev/null +++ b/crates/capi/src/codecs.rs @@ -0,0 +1,308 @@ +use crate::{PyObject, pystate::with_vm}; +use core::ffi::{CStr, c_char, c_int}; +use rustpython_vm::AsObject; + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_Register(search_function: *mut PyObject) -> c_int { + with_vm(|vm| { + let search_function = unsafe { &*search_function }.to_owned(); + vm.state.codec_registry.register(search_function, vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_Unregister(search_function: *mut PyObject) -> c_int { + with_vm(|vm| { + let search_function = unsafe { &*search_function }.to_owned(); + vm.state.codec_registry.unregister(search_function); + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_KnownEncoding(encoding: *const c_char) -> c_int { + with_vm(|vm| { + let encoding = unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + match vm.state.codec_registry.lookup(encoding, vm) { + Ok(_) => Ok(true), + Err(err) if err.fast_isinstance(vm.ctx.exceptions.lookup_error) => Ok(false), + Err(err) => Err(err), + } + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_Encode( + object: *mut PyObject, + encoding: *const c_char, + errors: *const c_char, +) -> *mut PyObject { + with_vm(|vm| { + let object = unsafe { &*object }.to_owned(); + let encoding = if encoding.is_null() { + "utf-8" + } else { + unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? + }; + let errors = if errors.is_null() { + None + } else { + let errors = unsafe { CStr::from_ptr(errors) } + .to_str() + .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; + Some(vm.ctx.new_utf8_str(errors)) + }; + vm.state.codec_registry.encode(object, encoding, errors, vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_Decode( + object: *mut PyObject, + encoding: *const c_char, + errors: *const c_char, +) -> *mut PyObject { + with_vm(|vm| { + let object = unsafe { &*object }.to_owned(); + let encoding = if encoding.is_null() { + "utf-8" + } else { + unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? + }; + let errors = if errors.is_null() { + None + } else { + let errors = unsafe { CStr::from_ptr(errors) } + .to_str() + .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; + Some(vm.ctx.new_utf8_str(errors)) + }; + vm.state.codec_registry.decode(object, encoding, errors, vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_Encoder(encoding: *const c_char) -> *mut PyObject { + with_vm(|vm| { + let encoding = unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + vm.state + .codec_registry + .lookup(encoding, vm) + .map(|codec| codec.get_encode_func().to_owned()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_Decoder(encoding: *const c_char) -> *mut PyObject { + with_vm(|vm| { + let encoding = unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + vm.state + .codec_registry + .lookup(encoding, vm) + .map(|codec| codec.get_decode_func().to_owned()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_IncrementalEncoder( + encoding: *const c_char, + errors: *const c_char, +) -> *mut PyObject { + with_vm(|vm| { + let encoding = unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + let errors = if errors.is_null() { + None + } else { + let errors = unsafe { CStr::from_ptr(errors) } + .to_str() + .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; + Some(vm.ctx.new_str(errors)) + }; + let codec = vm.state.codec_registry.lookup(encoding, vm)?; + codec.get_incremental_encoder(errors, vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_IncrementalDecoder( + encoding: *const c_char, + errors: *const c_char, +) -> *mut PyObject { + with_vm(|vm| { + let encoding = unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + let errors = if errors.is_null() { + None + } else { + let errors = unsafe { CStr::from_ptr(errors) } + .to_str() + .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; + Some(vm.ctx.new_str(errors)) + }; + let codec = vm.state.codec_registry.lookup(encoding, vm)?; + codec.get_incremental_decoder(errors, vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_StreamReader( + encoding: *const c_char, + stream: *mut PyObject, + errors: *const c_char, +) -> *mut PyObject { + with_vm(|vm| { + let encoding = if encoding.is_null() { + "utf-8" + } else { + unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? + }; + let errors = if errors.is_null() { + None + } else { + let errors = unsafe { CStr::from_ptr(errors) } + .to_str() + .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; + Some(vm.ctx.new_str(errors)) + }; + let stream = unsafe { &*stream }.to_owned(); + let codec = vm.state.codec_registry.lookup(encoding, vm)?; + let args = match errors { + Some(errors) => vec![stream, errors.into()], + None => vec![stream], + }; + vm.call_method(codec.as_tuple().as_object(), "streamreader", args) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_StreamWriter( + encoding: *const c_char, + stream: *mut PyObject, + errors: *const c_char, +) -> *mut PyObject { + with_vm(|vm| { + let encoding = if encoding.is_null() { + "utf-8" + } else { + unsafe { CStr::from_ptr(encoding) } + .to_str() + .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? + }; + let errors = if errors.is_null() { + None + } else { + let errors = unsafe { CStr::from_ptr(errors) } + .to_str() + .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; + Some(vm.ctx.new_str(errors)) + }; + let stream = unsafe { &*stream }.to_owned(); + let codec = vm.state.codec_registry.lookup(encoding, vm)?; + let args = match errors { + Some(errors) => vec![stream, errors.into()], + None => vec![stream], + }; + vm.call_method(codec.as_tuple().as_object(), "streamwriter", args) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_RegisterError(name: *const c_char, error: *mut PyObject) -> c_int { + with_vm(|vm| { + let name = unsafe { CStr::from_ptr(name) } + .to_str() + .map_err(|_| vm.new_system_error("name must be valid UTF-8"))?; + let error = unsafe { &*error }.to_owned(); + if !error.is_callable() { + return Err(vm.new_type_error("handler must be callable")); + } + vm.state + .codec_registry + .register_error(name.to_owned(), error); + Ok(()) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_LookupError(name: *const c_char) -> *mut PyObject { + with_vm(|vm| { + let name = unsafe { CStr::from_ptr(name) } + .to_str() + .map_err(|_| vm.new_system_error("name must be valid UTF-8"))?; + vm.state.codec_registry.lookup_error(name, vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_StrictErrors(exc: *mut PyObject) -> *mut PyObject { + with_vm(|vm| { + let err = vm.state.codec_registry.lookup_error("strict", vm)?; + let exc = unsafe { &*exc }.to_owned(); + err.call((exc,), vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_IgnoreErrors(exc: *mut PyObject) -> *mut PyObject { + with_vm(|vm| { + let err = vm.state.codec_registry.lookup_error("ignore", vm)?; + let exc = unsafe { &*exc }.to_owned(); + err.call((exc,), vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_ReplaceErrors(exc: *mut PyObject) -> *mut PyObject { + with_vm(|vm| { + let err = vm.state.codec_registry.lookup_error("replace", vm)?; + let exc = unsafe { &*exc }.to_owned(); + err.call((exc,), vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_XMLCharRefReplaceErrors(exc: *mut PyObject) -> *mut PyObject { + with_vm(|vm| { + let err = vm + .state + .codec_registry + .lookup_error("xmlcharrefreplace", vm)?; + let exc = unsafe { &*exc }.to_owned(); + err.call((exc,), vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_BackslashReplaceErrors(exc: *mut PyObject) -> *mut PyObject { + with_vm(|vm| { + let err = vm + .state + .codec_registry + .lookup_error("backslashreplace", vm)?; + let exc = unsafe { &*exc }.to_owned(); + err.call((exc,), vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyCodec_NameReplaceErrors(exc: *mut PyObject) -> *mut PyObject { + with_vm(|vm| { + let err = vm.state.codec_registry.lookup_error("namereplace", vm)?; + let exc = unsafe { &*exc }.to_owned(); + err.call((exc,), vm) + }) +} diff --git a/crates/capi/src/lib.rs b/crates/capi/src/lib.rs index 08bb09bf3b5..374fd3301d6 100644 --- a/crates/capi/src/lib.rs +++ b/crates/capi/src/lib.rs @@ -13,6 +13,7 @@ pub mod boolobject; pub mod bytearrayobject; pub mod bytesobject; pub mod ceval; +pub mod codecs; pub mod complexobject; pub mod critical_section; pub mod descrobject; From a65d9c4cc6c1ae3beeade709044ad838f0150c91 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Wed, 15 Jul 2026 17:15:32 +0200 Subject: [PATCH 2/2] Reduce code duplication --- crates/capi/src/abstract_.rs | 7 +- crates/capi/src/abstract_/mapping.rs | 21 +-- crates/capi/src/codecs.rs | 208 +++++++-------------------- crates/capi/src/descrobject.rs | 20 +-- crates/capi/src/dictobject.rs | 19 +-- crates/capi/src/import.rs | 16 +-- crates/capi/src/methodobject.rs | 15 +- crates/capi/src/object.rs | 27 ++-- crates/capi/src/pycapsule.rs | 5 +- crates/capi/src/pyerrors.rs | 27 ++-- crates/capi/src/unicodeobject.rs | 37 +---- crates/capi/src/util.rs | 30 ++++ crates/capi/src/warnings.rs | 23 ++- crates/vm/src/sequence.rs | 2 +- 14 files changed, 150 insertions(+), 307 deletions(-) diff --git a/crates/capi/src/abstract_.rs b/crates/capi/src/abstract_.rs index 36d949a3022..08b4e540029 100644 --- a/crates/capi/src/abstract_.rs +++ b/crates/capi/src/abstract_.rs @@ -1,6 +1,7 @@ +use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; use alloc::slice; -use core::ffi::{CStr, c_char, c_int}; +use core::ffi::{c_char, c_int}; pub use iter::*; pub use mapping::*; pub use number::*; @@ -208,9 +209,7 @@ pub unsafe extern "C" fn PyObject_DelItem(obj: *mut PyObject, key: *mut PyObject pub unsafe extern "C" fn PyObject_DelItemString(obj: *mut PyObject, key: *const c_char) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("mapping key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; obj.del_item(key, vm) }) } diff --git a/crates/capi/src/abstract_/mapping.rs b/crates/capi/src/abstract_/mapping.rs index 6fec18bffd6..840a9aed69c 100644 --- a/crates/capi/src/abstract_/mapping.rs +++ b/crates/capi/src/abstract_/mapping.rs @@ -1,5 +1,6 @@ +use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; -use core::ffi::{CStr, c_char, c_int}; +use core::ffi::{c_char, c_int}; use rustpython_vm::AsObject; #[unsafe(no_mangle)] @@ -60,9 +61,7 @@ pub unsafe extern "C" fn PyMapping_GetItemString( ) -> *mut PyObject { with_vm(|vm| { let obj = unsafe { &*obj }; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("mapping key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; obj.get_item(key, vm) }) } @@ -104,9 +103,7 @@ pub unsafe extern "C" fn PyMapping_GetOptionalItemString( *result = core::ptr::null_mut(); } let obj = unsafe { &*obj }; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("mapping key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; match obj.get_item(key, vm) { Ok(value) => { @@ -134,7 +131,7 @@ pub unsafe extern "C" fn PyMapping_HasKey(obj: *mut PyObject, key: *mut PyObject pub unsafe extern "C" fn PyMapping_HasKeyString(obj: *mut PyObject, key: *const c_char) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - if let Ok(key) = unsafe { CStr::from_ptr(key) }.to_str() { + if let Ok(key) = unsafe { key.try_as_str(vm) } { obj.get_item(key, vm).is_ok() } else { false @@ -166,9 +163,7 @@ pub unsafe extern "C" fn PyMapping_HasKeyStringWithError( ) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("mapping key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; match obj.get_item(key, vm) { Ok(_) => Ok(true), @@ -186,9 +181,7 @@ pub unsafe extern "C" fn PyMapping_SetItemString( ) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("mapping key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; let value = unsafe { &*value }.to_owned(); obj.set_item(key, value, vm) }) diff --git a/crates/capi/src/codecs.rs b/crates/capi/src/codecs.rs index 390e6108752..8433bf3baa4 100644 --- a/crates/capi/src/codecs.rs +++ b/crates/capi/src/codecs.rs @@ -1,6 +1,36 @@ +use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; -use core::ffi::{CStr, c_char, c_int}; -use rustpython_vm::AsObject; +use core::ffi::{c_char, c_int}; +use rustpython_vm::{AsObject, VirtualMachine}; + +fn call_codec_error_handler( + vm: &VirtualMachine, + handler_name: &str, + exc: *mut PyObject, +) -> rustpython_vm::PyResult { + vm.state + .codec_registry + .lookup_error(handler_name, vm)? + .call((unsafe { &*exc }.to_owned(),), vm) +} + +fn codec_stream( + vm: &VirtualMachine, + encoding: *const c_char, + stream: *mut PyObject, + errors: *const c_char, + method: &str, +) -> rustpython_vm::PyResult { + let encoding = unsafe { encoding.try_as_str_opt(vm) }?.unwrap_or("utf-8"); + let errors = unsafe { errors.try_as_str_opt(vm) }?.map(|errors| vm.ctx.new_str(errors)); + let stream = unsafe { &*stream }.to_owned(); + let codec = vm.state.codec_registry.lookup(encoding, vm)?; + let args = match errors { + Some(errors) => vec![stream, errors.into()], + None => vec![stream], + }; + vm.call_method(codec.as_tuple().as_object(), method, args) +} #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_Register(search_function: *mut PyObject) -> c_int { @@ -21,9 +51,7 @@ pub unsafe extern "C" fn PyCodec_Unregister(search_function: *mut PyObject) -> c #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_KnownEncoding(encoding: *const c_char) -> c_int { with_vm(|vm| { - let encoding = unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + let encoding = unsafe { encoding.try_as_str(vm) }?; match vm.state.codec_registry.lookup(encoding, vm) { Ok(_) => Ok(true), Err(err) if err.fast_isinstance(vm.ctx.exceptions.lookup_error) => Ok(false), @@ -40,21 +68,9 @@ pub unsafe extern "C" fn PyCodec_Encode( ) -> *mut PyObject { with_vm(|vm| { let object = unsafe { &*object }.to_owned(); - let encoding = if encoding.is_null() { - "utf-8" - } else { - unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? - }; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; - Some(vm.ctx.new_utf8_str(errors)) - }; + let encoding = unsafe { encoding.try_as_str_opt(vm) }?.unwrap_or("utf-8"); + let errors = + unsafe { errors.try_as_str_opt(vm) }?.map(|errors| vm.ctx.new_utf8_str(errors)); vm.state.codec_registry.encode(object, encoding, errors, vm) }) } @@ -67,21 +83,9 @@ pub unsafe extern "C" fn PyCodec_Decode( ) -> *mut PyObject { with_vm(|vm| { let object = unsafe { &*object }.to_owned(); - let encoding = if encoding.is_null() { - "utf-8" - } else { - unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? - }; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; - Some(vm.ctx.new_utf8_str(errors)) - }; + let encoding = unsafe { encoding.try_as_str_opt(vm) }?.unwrap_or("utf-8"); + let errors = + unsafe { errors.try_as_str_opt(vm) }?.map(|errors| vm.ctx.new_utf8_str(errors)); vm.state.codec_registry.decode(object, encoding, errors, vm) }) } @@ -89,9 +93,7 @@ pub unsafe extern "C" fn PyCodec_Decode( #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_Encoder(encoding: *const c_char) -> *mut PyObject { with_vm(|vm| { - let encoding = unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + let encoding = unsafe { encoding.try_as_str(vm) }?; vm.state .codec_registry .lookup(encoding, vm) @@ -102,9 +104,7 @@ pub unsafe extern "C" fn PyCodec_Encoder(encoding: *const c_char) -> *mut PyObje #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_Decoder(encoding: *const c_char) -> *mut PyObject { with_vm(|vm| { - let encoding = unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; + let encoding = unsafe { encoding.try_as_str(vm) }?; vm.state .codec_registry .lookup(encoding, vm) @@ -118,17 +118,8 @@ pub unsafe extern "C" fn PyCodec_IncrementalEncoder( errors: *const c_char, ) -> *mut PyObject { with_vm(|vm| { - let encoding = unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; - Some(vm.ctx.new_str(errors)) - }; + let encoding = unsafe { encoding.try_as_str(vm) }?; + let errors = unsafe { errors.try_as_str_opt(vm) }?.map(|s| vm.ctx.new_str(s)); let codec = vm.state.codec_registry.lookup(encoding, vm)?; codec.get_incremental_encoder(errors, vm) }) @@ -140,17 +131,8 @@ pub unsafe extern "C" fn PyCodec_IncrementalDecoder( errors: *const c_char, ) -> *mut PyObject { with_vm(|vm| { - let encoding = unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; - Some(vm.ctx.new_str(errors)) - }; + let encoding = unsafe { encoding.try_as_str(vm) }?; + let errors = unsafe { errors.try_as_str_opt(vm) }?.map(|s| vm.ctx.new_str(s)); let codec = vm.state.codec_registry.lookup(encoding, vm)?; codec.get_incremental_decoder(errors, vm) }) @@ -162,30 +144,7 @@ pub unsafe extern "C" fn PyCodec_StreamReader( stream: *mut PyObject, errors: *const c_char, ) -> *mut PyObject { - with_vm(|vm| { - let encoding = if encoding.is_null() { - "utf-8" - } else { - unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? - }; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; - Some(vm.ctx.new_str(errors)) - }; - let stream = unsafe { &*stream }.to_owned(); - let codec = vm.state.codec_registry.lookup(encoding, vm)?; - let args = match errors { - Some(errors) => vec![stream, errors.into()], - None => vec![stream], - }; - vm.call_method(codec.as_tuple().as_object(), "streamreader", args) - }) + with_vm(|vm| codec_stream(vm, encoding, stream, errors, "streamreader")) } #[unsafe(no_mangle)] @@ -194,38 +153,13 @@ pub unsafe extern "C" fn PyCodec_StreamWriter( stream: *mut PyObject, errors: *const c_char, ) -> *mut PyObject { - with_vm(|vm| { - let encoding = if encoding.is_null() { - "utf-8" - } else { - unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? - }; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; - Some(vm.ctx.new_str(errors)) - }; - let stream = unsafe { &*stream }.to_owned(); - let codec = vm.state.codec_registry.lookup(encoding, vm)?; - let args = match errors { - Some(errors) => vec![stream, errors.into()], - None => vec![stream], - }; - vm.call_method(codec.as_tuple().as_object(), "streamwriter", args) - }) + with_vm(|vm| codec_stream(vm, encoding, stream, errors, "streamwriter")) } #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_RegisterError(name: *const c_char, error: *mut PyObject) -> c_int { with_vm(|vm| { - let name = unsafe { CStr::from_ptr(name) } - .to_str() - .map_err(|_| vm.new_system_error("name must be valid UTF-8"))?; + let name = unsafe { name.try_as_str(vm) }?; let error = unsafe { &*error }.to_owned(); if !error.is_callable() { return Err(vm.new_type_error("handler must be callable")); @@ -240,69 +174,37 @@ pub unsafe extern "C" fn PyCodec_RegisterError(name: *const c_char, error: *mut #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_LookupError(name: *const c_char) -> *mut PyObject { with_vm(|vm| { - let name = unsafe { CStr::from_ptr(name) } - .to_str() - .map_err(|_| vm.new_system_error("name must be valid UTF-8"))?; + let name = unsafe { name.try_as_str(vm) }?; vm.state.codec_registry.lookup_error(name, vm) }) } #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_StrictErrors(exc: *mut PyObject) -> *mut PyObject { - with_vm(|vm| { - let err = vm.state.codec_registry.lookup_error("strict", vm)?; - let exc = unsafe { &*exc }.to_owned(); - err.call((exc,), vm) - }) + with_vm(|vm| call_codec_error_handler(vm, "strict", exc)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_IgnoreErrors(exc: *mut PyObject) -> *mut PyObject { - with_vm(|vm| { - let err = vm.state.codec_registry.lookup_error("ignore", vm)?; - let exc = unsafe { &*exc }.to_owned(); - err.call((exc,), vm) - }) + with_vm(|vm| call_codec_error_handler(vm, "ignore", exc)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_ReplaceErrors(exc: *mut PyObject) -> *mut PyObject { - with_vm(|vm| { - let err = vm.state.codec_registry.lookup_error("replace", vm)?; - let exc = unsafe { &*exc }.to_owned(); - err.call((exc,), vm) - }) + with_vm(|vm| call_codec_error_handler(vm, "replace", exc)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_XMLCharRefReplaceErrors(exc: *mut PyObject) -> *mut PyObject { - with_vm(|vm| { - let err = vm - .state - .codec_registry - .lookup_error("xmlcharrefreplace", vm)?; - let exc = unsafe { &*exc }.to_owned(); - err.call((exc,), vm) - }) + with_vm(|vm| call_codec_error_handler(vm, "xmlcharrefreplace", exc)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_BackslashReplaceErrors(exc: *mut PyObject) -> *mut PyObject { - with_vm(|vm| { - let err = vm - .state - .codec_registry - .lookup_error("backslashreplace", vm)?; - let exc = unsafe { &*exc }.to_owned(); - err.call((exc,), vm) - }) + with_vm(|vm| call_codec_error_handler(vm, "backslashreplace", exc)) } #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCodec_NameReplaceErrors(exc: *mut PyObject) -> *mut PyObject { - with_vm(|vm| { - let err = vm.state.codec_registry.lookup_error("namereplace", vm)?; - let exc = unsafe { &*exc }.to_owned(); - err.call((exc,), vm) - }) + with_vm(|vm| call_codec_error_handler(vm, "namereplace", exc)) } diff --git a/crates/capi/src/descrobject.rs b/crates/capi/src/descrobject.rs index ecc0b53f82a..863f286e13a 100644 --- a/crates/capi/src/descrobject.rs +++ b/crates/capi/src/descrobject.rs @@ -2,7 +2,8 @@ use crate::PyObject; use crate::methodobject::{PyMethodDef, build_method_def}; use crate::object::PyTypeObject; use crate::pystate::with_vm; -use core::ffi::{CStr, c_char, c_int, c_void}; +use crate::util::CStrExt; +use core::ffi::{c_char, c_int, c_void}; use core::ptr::NonNull; use rustpython_vm::builtins::{ DescriptorMemberDef, MemberGetter, MemberKind, MemberSetter, PyDescriptorOwned, PyGetSet, @@ -34,9 +35,7 @@ impl PyGetSetDef { ty: &'static Py, vm: &VirtualMachine, ) -> PyResult> { - let name = unsafe { CStr::from_ptr(self.name) } - .to_str() - .map_err(|_| vm.new_system_error("PyGetSetDef name was not valid UTF-8"))?; + let name = unsafe { self.name.try_as_str(vm) }?; let closure = self.closure as usize; let descriptor = match (self.get, self.set) { @@ -142,9 +141,7 @@ impl PyMemberDef { ty: &Py, vm: &VirtualMachine, ) -> PyResult> { - let name = unsafe { CStr::from_ptr(self.name) } - .to_str() - .map_err(|_| vm.new_system_error("PyMemberDef name was not valid UTF-8"))?; + let name = unsafe { self.name.try_as_str(vm) }?; let kind = match self.type_code { 6 => MemberKind::Object, 16 => MemberKind::ObjectEx, @@ -165,14 +162,7 @@ impl PyMemberDef { ); } - let doc = NonNull::new(self.doc.cast_mut()) - .map(|doc| { - unsafe { CStr::from_ptr(doc.as_ptr()) } - .to_str() - .map(|s| s.to_owned()) - .map_err(|_| vm.new_system_error("PyMemberDef doc was not valid UTF-8")) - }) - .transpose()?; + let doc = unsafe { self.doc.try_as_str_opt(vm) }?.map(str::to_owned); let descriptor = PyMemberDescriptor { common: PyDescriptorOwned { diff --git a/crates/capi/src/dictobject.rs b/crates/capi/src/dictobject.rs index 6cea9ea343c..ed29c693463 100644 --- a/crates/capi/src/dictobject.rs +++ b/crates/capi/src/dictobject.rs @@ -1,7 +1,8 @@ use crate::PyObject; use crate::object::define_py_check; use crate::pystate::with_vm; -use core::ffi::{CStr, c_char, c_int}; +use crate::util::CStrExt; +use core::ffi::{c_char, c_int}; use core::ptr::NonNull; use rustpython_vm::AsObject; use rustpython_vm::PyPayload; @@ -49,9 +50,7 @@ pub unsafe extern "C" fn PyDict_SetItemString( ) -> c_int { with_vm(|vm| { let dict = unsafe { &*dict }.try_downcast_ref::(vm)?; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("dictionary key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; let value = unsafe { &*val }.to_owned(); dict.inner_setitem(key, value, vm) }) @@ -94,9 +93,7 @@ pub unsafe extern "C" fn PyDict_GetItemString( ) -> *mut PyObject { with_vm(|vm| { let dict = unsafe { &*dict }.try_downcast_ref::(vm)?; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_unicode_decode_error("dictionary key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; match dict.inner_getitem_opt(key, vm)? { Some(value) => Ok(value.as_object().as_raw().cast_mut()), @@ -116,9 +113,7 @@ pub unsafe extern "C" fn PyDict_GetItemStringRef( *result = core::ptr::null_mut(); } let dict = unsafe { &*dict }.try_downcast_ref::(vm)?; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("dictionary key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; if let Some(value) = dict.inner_getitem_opt(key, vm)? { unsafe { @@ -231,9 +226,7 @@ pub unsafe extern "C" fn PyDict_DelItem(dict: *mut PyObject, key: *mut PyObject) pub unsafe extern "C" fn PyDict_DelItemString(dict: *mut PyObject, key: *const c_char) -> c_int { with_vm(|vm| { let dict = unsafe { &*dict }.try_downcast_ref::(vm)?; - let key = unsafe { CStr::from_ptr(key) } - .to_str() - .map_err(|_| vm.new_value_error("dictionary key must be valid UTF-8"))?; + let key = unsafe { key.try_as_str(vm) }?; dict.del_item(key, vm) }) } diff --git a/crates/capi/src/import.rs b/crates/capi/src/import.rs index c6d5ce85ed6..3a8dae651c9 100644 --- a/crates/capi/src/import.rs +++ b/crates/capi/src/import.rs @@ -1,5 +1,6 @@ +use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; -use core::ffi::{CStr, c_char}; +use core::ffi::c_char; use rustpython_vm::builtins::{PyCode, PyDict, PyModule, PyStr}; use rustpython_vm::import::import_code_obj; @@ -14,9 +15,7 @@ pub unsafe extern "C" fn PyImport_Import(name: *mut PyObject) -> *mut PyObject { #[unsafe(no_mangle)] pub unsafe extern "C" fn PyImport_AddModuleRef(name: *const c_char) -> *mut PyObject { with_vm(|vm| { - let name = unsafe { CStr::from_ptr(name) } - .to_str() - .map_err(|_| vm.new_system_error("PyImport_AddModuleRef called with non utf8 name"))?; + let name = unsafe { name.try_as_str(vm) }?; let sys_modules = vm .sys_module @@ -46,16 +45,11 @@ pub unsafe extern "C" fn PyImport_ExecCodeModuleEx( pathname: *const c_char, ) -> *mut PyObject { with_vm(|vm| { - let name = unsafe { CStr::from_ptr(name) }.to_str().map_err(|_| { - vm.new_system_error("PyImport_ExecCodeModuleEx called with non utf8 name") - })?; + let name = unsafe { name.try_as_str(vm) }?; let code = unsafe { &*co }.try_downcast_ref::(vm)?; let module = import_code_obj(vm, name, code.to_owned(), false)?; - if !pathname.is_null() { - let pathname = unsafe { CStr::from_ptr(pathname) }.to_str().map_err(|_| { - vm.new_system_error("PyImport_ExecCodeModuleEx called with non utf8 pathname") - })?; + if let Some(pathname) = unsafe { pathname.try_as_str_opt(vm) }? { module.set_attr("__file__", vm.ctx.new_str(pathname), vm)?; } diff --git a/crates/capi/src/methodobject.rs b/crates/capi/src/methodobject.rs index b234ba76a9c..cc3676ef51a 100644 --- a/crates/capi/src/methodobject.rs +++ b/crates/capi/src/methodobject.rs @@ -2,7 +2,8 @@ use crate::PyObject; use crate::object::PyTypeObject; use crate::object::define_py_check; use crate::pystate::with_vm; -use core::ffi::{CStr, c_char, c_int}; +use crate::util::CStrExt; +use core::ffi::{c_char, c_int}; use core::ptr::NonNull; use rustpython_vm::function::{FuncArgs, HeapMethodDef, PosArgs, PyMethodFlags}; use rustpython_vm::{AsObject, PyObjectRef, PyRef, PyResult, VirtualMachine}; @@ -46,17 +47,9 @@ pub(crate) fn build_method_def( ml: &PyMethodDef, has_self: bool, ) -> PyResult> { - let name = unsafe { CStr::from_ptr(ml.ml_name) } - .to_str() - .map_err(|_| vm.new_system_error("Method name was not valid UTF-8"))?; + let name = unsafe { ml.ml_name.try_as_str(vm) }?; - let doc = NonNull::new(ml.ml_doc.cast_mut()) - .map(|doc| { - unsafe { CStr::from_ptr(doc.as_ptr()) } - .to_str() - .map_err(|_| vm.new_system_error("Method doc was not valid UTF-8")) - }) - .transpose()?; + let doc = unsafe { ml.ml_doc.try_as_str_opt(vm) }?; let flags = PyMethodFlags::from_bits(ml.ml_flags as u32) .ok_or_else(|| vm.new_system_error("PyMethodDef contains unknown flags"))?; diff --git a/crates/capi/src/object.rs b/crates/capi/src/object.rs index 27417a6ad33..eabfbef23a1 100644 --- a/crates/capi/src/object.rs +++ b/crates/capi/src/object.rs @@ -1,6 +1,7 @@ use crate::PyObject; use crate::pystate::with_vm; -use core::ffi::{CStr, c_char, c_int, c_uint, c_void}; +use crate::util::CStrExt; +use core::ffi::{c_char, c_int, c_uint, c_void}; use core::ptr::NonNull; pub use pytype::*; use rustpython_vm::builtins::{PyStr, object_generic_set_dict, object_get_dict}; @@ -81,11 +82,7 @@ pub unsafe extern "C" fn PyObject_GetAttrString( ) -> *mut PyObject { with_vm(|vm| { let obj = unsafe { &*obj }; - let name = unsafe { - CStr::from_ptr(attr_name) - .to_str() - .map_err(|_| vm.new_value_error("attribute name must be valid UTF-8"))? - }; + let name = unsafe { attr_name.try_as_str(vm) }?; obj.get_attr(name, vm) }) } @@ -134,9 +131,7 @@ pub unsafe extern "C" fn PyObject_GetOptionalAttrString( *result = core::ptr::null_mut(); } let obj = unsafe { &*obj }; - let name = unsafe { CStr::from_ptr(attr_name) } - .to_str() - .map_err(|_| vm.new_value_error("attribute name must be valid UTF-8"))?; + let name = unsafe { attr_name.try_as_str(vm) }?; if let Some(attr) = vm.get_attribute_opt(obj.to_owned(), name)? { unsafe { *result = attr.into_raw().as_ptr(); @@ -156,9 +151,7 @@ pub unsafe extern "C" fn PyObject_SetAttrString( ) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - let name = unsafe { CStr::from_ptr(attr_name) } - .to_str() - .map_err(|_| vm.new_value_error("attribute name must be valid UTF-8"))?; + let name = unsafe { attr_name.try_as_str(vm) }?; let value = unsafe { &*value }.to_owned(); obj.set_attr(name, value, vm) }) @@ -194,9 +187,7 @@ pub unsafe extern "C" fn PyObject_DelAttrString( ) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - let name = unsafe { CStr::from_ptr(attr_name) } - .to_str() - .map_err(|_| vm.new_value_error("attribute name must be valid UTF-8"))?; + let name = unsafe { attr_name.try_as_str(vm) }?; obj.del_attr(name, vm) }) } @@ -259,7 +250,7 @@ pub unsafe extern "C" fn PyObject_HasAttrString( ) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - let Ok(name) = unsafe { CStr::from_ptr(attr_name) }.to_str() else { + let Ok(name) = (unsafe { attr_name.try_as_str(vm) }) else { return false; }; @@ -280,9 +271,7 @@ pub unsafe extern "C" fn PyObject_HasAttrStringWithError( ) -> c_int { with_vm(|vm| { let obj = unsafe { &*obj }; - let name = unsafe { CStr::from_ptr(attr_name) } - .to_str() - .map_err(|_| vm.new_value_error("attribute name must be valid UTF-8"))?; + let name = unsafe { attr_name.try_as_str(vm) }?; obj.has_attr(name, vm) }) } diff --git a/crates/capi/src/pycapsule.rs b/crates/capi/src/pycapsule.rs index a1b5effd88c..b36dea3d946 100644 --- a/crates/capi/src/pycapsule.rs +++ b/crates/capi/src/pycapsule.rs @@ -1,5 +1,6 @@ use crate::PyObject; use crate::pystate::with_vm; +use crate::util::CStrExt; use core::ffi::{CStr, c_char, c_int, c_void}; use core::ptr::NonNull; use rustpython_vm::builtins::PyCapsule; @@ -93,9 +94,7 @@ pub unsafe extern "C" fn PyCapsule_IsValid(capsule: *mut PyObject, name: *const #[unsafe(no_mangle)] pub unsafe extern "C" fn PyCapsule_Import(name: *const c_char, _no_block: c_int) -> *mut c_void { with_vm(|vm| { - let capsule_name = unsafe { CStr::from_ptr(name) } - .to_str() - .map_err(|_| vm.new_system_error("capsule name is not valid UTF-8"))?; + let capsule_name = unsafe { name.try_as_str(vm) }?; let (module_name, attrs_path) = capsule_name.split_once('.').ok_or_else(|| { vm.new_import_error( "capsule name is missing attribute path", diff --git a/crates/capi/src/pyerrors.rs b/crates/capi/src/pyerrors.rs index d7efd1a0d6f..55428ee7604 100644 --- a/crates/capi/src/pyerrors.rs +++ b/crates/capi/src/pyerrors.rs @@ -1,7 +1,8 @@ use crate::object::define_py_check; +use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; use core::convert::Infallible; -use core::ffi::{CStr, c_char, c_int}; +use core::ffi::{c_char, c_int}; use core::ptr::NonNull; use core::slice; use rustpython_vm::builtins::{PyBaseException, PyTuple, PyType}; @@ -144,10 +145,7 @@ pub unsafe extern "C" fn PyErr_SetObject(exception: *mut PyObject, value: *mut P pub unsafe extern "C" fn PyErr_SetString(exception: *mut PyObject, message: *const c_char) { with_vm::, _>(|vm| { let exc_type = unsafe { &*exception }.try_downcast_ref::(vm)?; - - let Ok(message) = unsafe { CStr::from_ptr(message) }.to_str() else { - return Err(vm.new_type_error("Exception message is not valid UTF-8")); - }; + let message = unsafe { message.try_as_str(vm) }?; let exc = vm.invoke_exception( exc_type.to_owned(), @@ -210,13 +208,10 @@ pub unsafe extern "C" fn PyErr_NewException( dict: *mut PyObject, ) -> *mut PyObject { with_vm(|vm| { - let (module, name) = unsafe { - CStr::from_ptr(name) - .to_str() - .expect("Exception name is not valid UTF-8") - .rsplit_once('.') - .expect("Exception name must be of the form 'module.ExceptionName'") - }; + let (module, name) = unsafe { name.try_as_str(vm) } + .expect("Exception name is not valid UTF-8") + .rsplit_once('.') + .expect("Exception name must be of the form 'module.ExceptionName'"); let bases = unsafe { base.as_ref() }.map(|bases| { if let Some(ty) = bases.downcast_ref::() { @@ -332,12 +327,8 @@ pub unsafe extern "C" fn PyUnicodeDecodeError_Create( reason: *const c_char, ) -> *mut PyObject { with_vm(|vm| { - let encoding = unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))?; - let reason = unsafe { CStr::from_ptr(reason) } - .to_str() - .map_err(|_| vm.new_system_error("reason must be valid UTF-8"))?; + let encoding = unsafe { encoding.try_as_str(vm) }?; + let reason = unsafe { reason.try_as_str(vm) }?; let length: usize = length .try_into() .map_err(|_| vm.new_system_error("length must be non-negative"))?; diff --git a/crates/capi/src/unicodeobject.rs b/crates/capi/src/unicodeobject.rs index 787e31ea571..1a5e43c0e9d 100644 --- a/crates/capi/src/unicodeobject.rs +++ b/crates/capi/src/unicodeobject.rs @@ -1,4 +1,5 @@ use crate::object::define_py_check; +use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; use core::ffi::{CStr, c_char, c_int}; use core::ptr::NonNull; @@ -70,21 +71,9 @@ pub unsafe extern "C" fn PyUnicode_AsEncodedString( let unicode = unsafe { &*unicode } .try_downcast_ref::(vm)? .to_owned(); - let encoding = if encoding.is_null() { - "utf-8" - } else { - unsafe { CStr::from_ptr(encoding) } - .to_str() - .expect("encoding must be valid UTF-8") - }; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .expect("errors must be valid UTF-8"); - Some(vm.ctx.new_utf8_str(errors)) - }; + let encoding = unsafe { encoding.try_as_str_opt(vm) }?.unwrap_or("utf-8"); + let errors = + unsafe { errors.try_as_str_opt(vm) }?.map(|errors| vm.ctx.new_utf8_str(errors)); vm.state .codec_registry .encode_text(unicode, encoding, errors, vm) @@ -177,21 +166,9 @@ pub unsafe extern "C" fn PyUnicode_FromEncodedObject( return Err(vm.new_type_error("decoding str is not supported")); } - let encoding = if encoding.is_null() { - "utf-8" - } else { - unsafe { CStr::from_ptr(encoding) } - .to_str() - .map_err(|_| vm.new_system_error("encoding must be valid UTF-8"))? - }; - let errors = if errors.is_null() { - None - } else { - let errors = unsafe { CStr::from_ptr(errors) } - .to_str() - .map_err(|_| vm.new_system_error("errors must be valid UTF-8"))?; - Some(vm.ctx.new_utf8_str(errors)) - }; + let encoding = unsafe { encoding.try_as_str_opt(vm) }?.unwrap_or("utf-8"); + let errors = + unsafe { errors.try_as_str_opt(vm) }?.map(|errors| vm.ctx.new_utf8_str(errors)); obj.try_bytes_like(vm, |b| { vm.state.codec_registry.decode_text( diff --git a/crates/capi/src/util.rs b/crates/capi/src/util.rs index 6bbda7654fe..32ff775676b 100644 --- a/crates/capi/src/util.rs +++ b/crates/capi/src/util.rs @@ -1,6 +1,7 @@ use crate::PyObject; use core::convert::Infallible; use core::ffi::{CStr, c_char, c_double, c_int, c_long, c_ulong, c_void}; +use core::ptr::NonNull; use rustpython_vm::{Py, PyObjectRef, PyRef, PyResult, VirtualMachine}; pub(crate) trait FfiResult { @@ -222,6 +223,35 @@ where } } +pub(crate) trait CStrExt<'a> { + unsafe fn try_as_str(self, vm: &VirtualMachine) -> PyResult<&'a str>; + unsafe fn try_as_str_opt(self, vm: &VirtualMachine) -> PyResult>; +} + +impl<'a> CStrExt<'a> for *mut c_char { + unsafe fn try_as_str(self, vm: &VirtualMachine) -> PyResult<&'a str> { + unsafe { self.try_as_str_opt(vm) }? + .ok_or_else(|| vm.new_system_error("argument must not be null")) + } + + unsafe fn try_as_str_opt(self, vm: &VirtualMachine) -> PyResult> { + NonNull::new(self) + .map(|ptr| unsafe { CStr::from_ptr(ptr.as_ptr()) }.to_str()) + .transpose() + .map_err(|_| vm.new_system_error("argument must be valid UTF-8")) + } +} + +impl<'a> CStrExt<'a> for *const c_char { + unsafe fn try_as_str(self, vm: &VirtualMachine) -> PyResult<&'a str> { + unsafe { self.cast_mut().try_as_str(vm) } + } + + unsafe fn try_as_str_opt(self, vm: &VirtualMachine) -> PyResult> { + unsafe { self.cast_mut().try_as_str_opt(vm) } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/capi/src/warnings.rs b/crates/capi/src/warnings.rs index 4966cd60d6d..f9ed82b9fa9 100644 --- a/crates/capi/src/warnings.rs +++ b/crates/capi/src/warnings.rs @@ -1,5 +1,6 @@ +use crate::util::CStrExt; use crate::{PyObject, pystate::with_vm}; -use core::ffi::{CStr, c_char, c_int}; +use core::ffi::{c_char, c_int}; use rustpython_vm::builtins::{PyType, PyTypeRef}; use rustpython_vm::warn::{warn, warn_explicit}; use rustpython_vm::{AsObject, PyResult}; @@ -32,9 +33,7 @@ pub unsafe extern "C" fn PyErr_WarnEx( stack_level: isize, ) -> c_int { with_vm(|vm| { - let message = unsafe { CStr::from_ptr(message) } - .to_str() - .map_err(|_| vm.new_system_error("warning message is not valid UTF-8"))?; + let message = unsafe { message.try_as_str(vm) }?; let category = resolve_warning_category(vm, category)?; @@ -58,17 +57,11 @@ pub unsafe extern "C" fn PyErr_WarnExplicit( registry: *mut PyObject, ) -> c_int { with_vm(|vm| { - let message = unsafe { CStr::from_ptr(message) } - .to_str() - .map_err(|_| vm.new_system_error("warning message is not valid UTF-8"))?; - let filename = unsafe { CStr::from_ptr(filename) } - .to_str() - .map_err(|_| vm.new_system_error("filename is not valid UTF-8"))?; - - let module = unsafe { module.as_ref().map(|ptr| CStr::from_ptr(ptr).to_str()) } - .transpose() - .map_err(|_| vm.new_system_error("module is not valid UTF-8"))? - .map(|module| vm.ctx.new_str(module).into()); + let message = unsafe { message.try_as_str(vm) }?; + let filename = unsafe { filename.try_as_str(vm) }?; + + let module = + unsafe { module.try_as_str_opt(vm) }?.map(|module| vm.ctx.new_str(module).into()); let category = resolve_warning_category(vm, category)?; diff --git a/crates/vm/src/sequence.rs b/crates/vm/src/sequence.rs index 0bc35181b38..4e6ed97f21c 100644 --- a/crates/vm/src/sequence.rs +++ b/crates/vm/src/sequence.rs @@ -124,7 +124,7 @@ where let n = vm.check_repeat_or_overflow_error(self.as_ref().len(), n)?; if n > 1 && core::mem::size_of_val(self.as_ref()) >= MAX_MEMORY_SIZE / n { - // TODO: make a global static NoMemory shared exc object and return its reference. + // TODO: make a global static NoMemory shared exc object and return its reference. return Err(vm.new_memory_error("")); }