GeezerVison is showing github.com — Replace runtime type-name checks with type identity by youknowdot · Pull Request #9723 · RustPython/RustPython
Replace runtime type-name checks with type identity
#9723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement . We’ll occasionally send you account related emails.
Already on GitHub?
Sign in
to your account
Uh oh!
There was an error while loading. Please reload this page.
Show file tree
Hide file tree
Changes from all commits
Commits
File filter
Filter by extension
.py
(4)
.rs
(8)
All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Hide whitespace
Diff view
Hide whitespace
There are no files selected for viewing
Original file line number
Diff line number
Diff line change
Expand Up
@@ -157,7 +157,6 @@ class IPersist(IUnknown):
self.assertEqual(0, ppst.Release())
@unittest.expectedFailure # TODO: RUSTPYTHON; - COM iid parameter handling not implemented
def test_with_paramflags_and_iid(self):
class IUnknown(c_void_p):
QueryInterface = proto_query_interface(None, IID_IUnknown)
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -1835,7 +1835,11 @@ pub const HRESULT_S_OK: i32 = crate::windows::HRESULT_S_OK;
#[cfg(windows)]
pub fn format_error_message(code: Option<u32>) -> Option<String> {
crate::windows::format_error_message(code)
let code = code
.filter(|&code| code != 0)
.unwrap_or_else(crate::windows::get_last_error);
crate::windows::format_error_message_with_language(code, 1 << 10)
.map(|message| message.trim_end().to_owned())
}
#[cfg(windows)]
Expand Down
Expand Up
@@ -3860,3 +3864,157 @@ mod tests {
}
}
}
/// Owned UTF-16 fields returned by COM's error-information interfaces.
#[cfg(windows)]
#[derive(Default)]
pub struct ComErrorDetails {
pub description: Option<Vec<u16>>,
pub source: Option<Vec<u16>>,
pub helpfile: Option<Vec<u16>>,
pub help_context: u32,
pub progid: Option<Vec<u16>>,
}
/// Format a COM error like ctypes' FormatError (retain trailing punctuation).
#[cfg(windows)]
pub fn format_com_error_message(status: i32) -> Option<String> {
crate::windows::format_error_message_with_language(status as u32, 1 << 10)
.map(|message| message.trim_end().to_owned())
}
/// Obtain the error information associated with a failed COM method call.
///
/// The caller must detach its interpreter while this runs: COM methods can
/// call back into Python. No borrowed Python buffers may be held here.
///
/// # Safety
/// `com_ptr` must identify a live IUnknown-compatible COM interface for the
/// duration of this call. `iid` is the native byte representation of its GUID.
#[cfg(windows)]
pub unsafe fn com_error_details(com_ptr: usize, iid: &[u8; 16]) -> ComErrorDetails {
use windows_sys::core::GUID;
type QueryInterface =
unsafe extern "system" fn(*mut c_void, *const GUID, *mut *mut c_void) -> i32;
type Release = unsafe extern "system" fn(*mut c_void) -> u32;
type SupportsErrorInfo = unsafe extern "system" fn(*mut c_void, *const GUID) -> i32;
type GetGuid = unsafe extern "system" fn(*mut c_void, *mut GUID) -> i32;
type GetString = unsafe extern "system" fn(*mut c_void, *mut *mut u16) -> i32;
type GetContext = unsafe extern "system" fn(*mut c_void, *mut u32) -> i32;
struct ComRef(*mut c_void);
impl Drop for ComRef {
fn drop(&mut self) {
// SAFETY: ComRef owns one reference on an IUnknown-compatible
// interface, whose third vtable entry is Release.
unsafe {
let release: Release = core::mem::transmute(method(self.0, 2));
release(self.0);
}
}
}
unsafe fn method(interface: *mut c_void, index: usize) -> usize {
// SAFETY: the caller provides a live interface and a valid slot for
// that interface's documented COM ABI.
unsafe { *(*(interface as *const *const usize)).add(index) }
}
unsafe fn string_field(interface: *mut c_void, index: usize) -> Option<Vec<u16>> {
let mut value = core::ptr::null_mut();
// SAFETY: IErrorInfo slots 4–6 all return an owned BSTR through an
// initialized output pointer. Free even if the method reports failure.
unsafe {
let get: GetString = core::mem::transmute(method(interface, index));
get(interface, &mut value);
if value.is_null() {
return None;
}
let units = core::slice::from_raw_parts(value, sys_string_len(value));
// CPython builds these fields from NUL-terminated wide strings.
let end = units
.iter()
.position(|&unit| unit == 0)
.unwrap_or(units.len());
let result = units[..end].to_vec();
sys_free_string(value);
Some(result)
}
}
let mut details = ComErrorDetails::default();
if com_ptr == 0 {
return details;
}
// IID_ISupportErrorInfo = {DF0B3D60-548F-101B-8E65-08002B2BD119}.
let support_iid = GUID {
data1: 0xdf0b3d60,
data2: 0x548f,
data3: 0x101b,
data4: [0x8e, 0x65, 0x08, 0x00, 0x2b, 0x2b, 0xd1, 0x19],
};
// SAFETY: GUID is a 16-byte POD value; the input need not be aligned.
let iid = unsafe { core::ptr::read_unaligned(iid.as_ptr().cast::<GUID>()) };
let interface = com_ptr as *mut c_void;
let mut support = core::ptr::null_mut();
// SAFETY: the live input interface implements IUnknown. A successful
// QueryInterface returns one owned ISupportErrorInfo reference.
let status = unsafe {
let query: QueryInterface = core::mem::transmute(method(interface, 0));
query(interface, &support_iid, &mut support)
};
if status < 0 || support.is_null() {
return details;
}
let support = ComRef(support);
// SAFETY: ISupportErrorInfo's fourth slot accepts the requested IID.
let status = unsafe {
let supports: SupportsErrorInfo = core::mem::transmute(method(support.0, 3));
supports(support.0, &iid)
};
drop(support);
if status < 0 {
return details;
}
let Some(error_info) = get_error_info() else {
return details;
};
if error_info.is_null() {
return details;
}
let error_info = ComRef(error_info);
let mut guid = GUID {
data1: 0,
data2: 0,
data3: 0,
data4: [0; 8],
};
// SAFETY: GetErrorInfo supplied a live IErrorInfo. Its slots have the
// documented signatures, and all output storage is initialized.
let guid_status = unsafe {
details.description = string_field(error_info.0, 5);
let get_guid: GetGuid = core::mem::transmute(method(error_info.0, 3));
let status = get_guid(error_info.0, &mut guid);
let get_context: GetContext = core::mem::transmute(method(error_info.0, 7));
get_context(error_info.0, &mut details.help_context);
details.helpfile = string_field(error_info.0, 6);
details.source = string_field(error_info.0, 4);
status
};
drop(error_info);
if guid_status >= 0 {
// SAFETY: GUID is POD; copy the initialized native representation.
let guid_bytes =
unsafe { core::ptr::read_unaligned((&guid as *const GUID).cast::<[u8; 16]>()) };
if let Some(progid) = prog_id_from_clsid(&guid_bytes) {
// SAFETY: ProgIDFromCLSID returns a NUL-terminated allocation
// owned by this call and released with CoTaskMemFree.
unsafe {
details.progid = Some(WideCStr::from_ptr_str(progid).as_slice().to_vec());
co_task_mem_free(progid.cast());
}
}
}
details
}
Original file line number
Diff line number
Diff line change
Expand Up
@@ -1316,7 +1316,7 @@ impl Representable for PySet {
}
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
let name = (class_name != "set" ).then_some(class_name);
let name = (!class.is(vm.ctx.types.set_type) ).then_some(class_name);
zelf.inner.repr(name, vm)
} else {
Ok(Wtf8Buf::from(format!("{class_name}(...)")))
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -2631,7 +2631,7 @@ pub(super) mod types {
// We need this method, because of how `CPython` copies `init`
// from `BaseException` in `SimpleExtendsException` macro.
// See: `BaseException_new`
if * cls.name() == * vm.ctx.exceptions.os_error.name( ) {
if cls.is( vm.ctx.exceptions.os_error) {
let args_vec = args.args.to_vec();
let len = args_vec.len();
if (2..=5).contains(&len) {
Expand Down
Original file line number
Diff line number
Diff line change
Expand Up
@@ -109,6 +109,8 @@ pub(crate) mod _ctypes {
use crate::convert::ToPyObject;
use crate::function::{ArgStrictInt, Either, OptionalArg};
use crate::types::Representable;
#[cfg(windows)]
use crate::types::{Constructor, Initializer};
use crate::{AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine};
use num_traits::ToPrimitive;
Expand Down
Expand Up
@@ -294,76 +296,49 @@ pub(crate) mod _ctypes {
)
}
#[cfg(target_os = "windows")]
#[pyattr(name = "COMError", once)]
fn com_error(vm: &VirtualMachine) -> PyTypeRef {
use crate::PyObject;
use crate::builtins::type_::PyAttributes;
use crate::function::FuncArgs;
use crate::types::{PyTypeFlags, PyTypeSlots};
// Sets hresult, text, details as instance attributes in __init__
// This function has InitFunc signature for direct slots.init use
fn comerror_init(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
let (hresult, text, details): (
Option<PyObjectRef>,
Option<PyObjectRef>,
Option<PyObjectRef>,
) = args.bind(vm)?;
let hresult = hresult.unwrap_or_else(|| vm.ctx.none());
let text = text.unwrap_or_else(|| vm.ctx.none());
let details = details.unwrap_or_else(|| vm.ctx.none());
// Set instance attributes
zelf.set_attr("hresult", hresult, vm)?;
zelf.set_attr("text", text.clone(), vm)?;
zelf.set_attr("details", details.clone(), vm)?;
/// Raised when a COM method call failed.
#[cfg(windows)]
#[pyattr]
#[pyexception(name = "COMError", module = "_ctypes", base = crate::builtins::PyException)]
#[derive(Debug)]
#[repr(transparent)]
struct PyComError(crate::builtins::PyException);
// self.args = args[1:] = (text, details)
// via: PyObject_SetAttrString(self, "args", PySequence_GetSlice(args, 1, size))
let args_tuple: PyObjectRef = vm.ctx.new_tuple(vec![text, details]).into();
zelf.set_attr("args", args_tuple, vm)?;
#[cfg(windows)]
#[pyexception(with(Initializer), flags(HEAPTYPE))]
impl PyComError {}
#[cfg(windows)]
impl Initializer for PyComError {
type Args = crate::function::FuncArgs;
fn slot_init(
zelf: &crate::PyObject,
args: Self::Args,
vm: &VirtualMachine,
) -> PyResult<()> {
let (hresult, text, details): (PyObjectRef, PyObjectRef, PyObjectRef) =
args.bind(vm)?;
let full_args = vec![hresult.clone(), text.clone(), details.clone()];
// Match the observable attribute assignments before restoring the
// base payload directly, without invoking an overriding descriptor.
let short_args: PyObjectRef =
vm.ctx.new_tuple(vec![text.clone(), details.clone()]).into();
zelf.set_attr("args", short_args, vm)?;
zelf.set_attr("hresult", hresult, vm)?;
zelf.set_attr("text", text, vm)?;
zelf.set_attr("details", details, vm)?;
crate::builtins::PyBaseException::slot_init(
zelf,
crate::function::FuncArgs::from(full_args),
vm,
)?;
Ok(())
}
// Create exception type with IMMUTABLETYPE flag
let mut attrs = PyAttributes::default();
attrs.insert(
vm.ctx.intern_str("__module__"),
vm.ctx.new_str("_ctypes").into(),
);
attrs.insert(
vm.ctx.intern_str("__doc__"),
vm.ctx
.new_str("Raised when a COM method call failed.")
.into(),
);
// Create slots with IMMUTABLETYPE flag
let slots = PyTypeSlots {
name: "COMError",
flags: crate::types::AtomicPyTypeFlags::from_plain(
PyTypeFlags::HEAP_TYPE_DICT_IMMUTABLE,
),
..PyTypeSlots::default()
};
let exc_type = PyType::new_heap(
"COMError",
vec![vm.ctx.exceptions.exception_type.to_owned()],
attrs,
slots,
vm.ctx.types.type_type.to_owned(),
&vm.ctx,
)
.unwrap();
// Set our custom init after new_heap, which runs init_slots that would
// otherwise overwrite slots.init with init_wrapper (due to __init__ in MRO).
exc_type.slots.init.store(Some(comerror_init));
exc_type
fn init(_zelf: &Py<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
unreachable!("slot_init is defined")
}
}
#[pyfunction]
Expand Down
Expand Up
@@ -608,7 +583,7 @@ pub(crate) mod _ctypes {
#[cfg(target_os = "windows")]
#[pyfunction(name = "_check_HRESULT")]
fn check_hresult(hr: i32, vm: &VirtualMachine) -> PyResult<i32> {
pub(super) fn check_hresult(hr: i32, vm: &VirtualMachine) -> PyResult<i32> {
if hr < 0 {
let errno = crate::host_env::os::winerror_to_errno(hr);
let message = crate::host_env::uuid::format_error_message(hr);
Expand Down
Expand Up
@@ -1042,9 +1017,11 @@ pub(crate) mod _ctypes {
#[cfg(windows)]
#[pyfunction(name = "FormatError")]
fn format_error_func(code: OptionalArg<u32>, _vm: &VirtualMachine) -> String {
rustpython_host_env::ctypes::format_error_message(code.into_option())
.unwrap_or_else(|| "<no description>".to_string())
fn format_error_func(code: OptionalArg<i32>, _vm: &VirtualMachine) -> String {
rustpython_host_env::ctypes::format_error_message(
code.into_option().map(|code| code as u32),
)
.unwrap_or_else(|| "<no description>".to_string())
}
#[cfg(windows)]
Expand Down
Loading
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Loading
Oops, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.