diff --git a/objc-sys/Cargo.toml b/objc-sys/Cargo.toml index b755c5cec..21bd61817 100644 --- a/objc-sys/Cargo.toml +++ b/objc-sys/Cargo.toml @@ -41,6 +41,8 @@ winobjc = ["gnustep-1-8"] # TODO objfw = [] +unstable-custom-runtime = [] + [package.metadata.docs.rs] default-target = "x86_64-apple-darwin" diff --git a/objc-sys/build.rs b/objc-sys/build.rs index 437cba691..6db7673fe 100644 --- a/objc-sys/build.rs +++ b/objc-sys/build.rs @@ -239,6 +239,7 @@ fn main() { println!("cargo:cc_args={}", cc_args); // DEP_OBJC_[version]_CC_ARGS + #[cfg(not(any(feature = "unstable-custom-runtime", miri)))] if let Runtime::ObjFW(_) = &runtime { // Link to libobjfw-rt println!("cargo:rustc-link-lib=dylib=objfw-rt"); diff --git a/objc-sys/src/lib.rs b/objc-sys/src/lib.rs index 12f0f5add..ca2b7541d 100644 --- a/objc-sys/src/lib.rs +++ b/objc-sys/src/lib.rs @@ -19,6 +19,11 @@ #![allow(non_upper_case_globals)] #![allow(non_snake_case)] #![doc(html_root_url = "https://docs.rs/objc-sys/0.2.0-alpha.1")] +#![cfg_attr(any(feature = "unstable-custom-runtime", miri), feature(once_cell))] +#![cfg_attr( + any(feature = "unstable-custom-runtime", miri), + feature(strict_provenance) +)] // TODO: Replace `extern "C"` with `extern "C-unwind"` where applicable. // See https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html. @@ -101,6 +106,8 @@ mod exception; mod message; mod method; +#[cfg(any(feature = "unstable-custom-runtime", miri))] +pub mod miri; mod object; mod property; mod protocol; diff --git a/objc-sys/src/miri/class.rs b/objc-sys/src/miri/class.rs new file mode 100644 index 000000000..478db002a --- /dev/null +++ b/objc-sys/src/miri/class.rs @@ -0,0 +1,45 @@ +use core::ptr; +use std::os::raw::c_char; + +use crate::{objc_class, objc_selector, BOOL, IMP}; + +#[no_mangle] +pub extern "C" fn objc_getClass(name: *const c_char) -> *const objc_class { + ptr::null() +} + +#[no_mangle] +pub extern "C" fn objc_allocateClassPair( + superclass: *const objc_class, + name: *const c_char, + extra_bytes: usize, +) -> *mut objc_class { + ptr::null_mut() +} + +#[no_mangle] +pub extern "C" fn objc_disposeClassPair(cls: *mut objc_class) {} + +#[no_mangle] +pub extern "C" fn objc_registerClassPair(cls: *mut objc_class) {} + +#[no_mangle] +pub fn class_addIvar( + cls: *mut objc_class, + name: *const c_char, + size: usize, + alignment: u8, + types: *const c_char, +) -> BOOL { + false as BOOL +} + +#[no_mangle] +pub fn class_addMethod( + cls: *mut objc_class, + name: *const objc_selector, + imp: IMP, + types: *const c_char, +) -> BOOL { + false as BOOL +} diff --git a/objc-sys/src/miri/message.rs b/objc-sys/src/miri/message.rs new file mode 100644 index 000000000..1ab2a58e1 --- /dev/null +++ b/objc-sys/src/miri/message.rs @@ -0,0 +1,31 @@ +use core::mem; + +use crate::{objc_class, objc_object, objc_selector}; + +pub fn custom_msg_send(obj: *mut objc_object, sel: *const objc_selector, args: T) -> R { + if obj.is_null() { + unsafe { mem::zeroed() } + } + if sel.is_null() { + panic!("Null selector") + } + todo!() +} + +pub fn custom_msg_send_super( + obj: *mut objc_object, + superclass: *const objc_class, + sel: *const objc_selector, + args: T, +) -> R { + if obj.is_null() { + unsafe { mem::zeroed() } + } + if superclass.is_null() { + panic!("Nil superclass") + } + if sel.is_null() { + panic!("Null selector") + } + todo!() +} diff --git a/objc-sys/src/miri/mod.rs b/objc-sys/src/miri/mod.rs new file mode 100644 index 000000000..6d1b9f884 --- /dev/null +++ b/objc-sys/src/miri/mod.rs @@ -0,0 +1,9 @@ +//! A Rust implementation of the parts of Objective-C runtime that we use. +//! +//! So that Miri works on the pure-Rust Objective-C code that people write. + +mod class; +mod message; +mod selector; + +pub use message::{custom_msg_send, custom_msg_send_super}; diff --git a/objc-sys/src/miri/selector.rs b/objc-sys/src/miri/selector.rs new file mode 100644 index 000000000..95eaec7fd --- /dev/null +++ b/objc-sys/src/miri/selector.rs @@ -0,0 +1,49 @@ +use core::ptr; +use std::boxed::Box; +use std::ffi::CStr; +use std::lazy::SyncLazy; +use std::os::raw::c_char; +use std::sync::Mutex; +use std::vec::Vec; + +use crate::objc_selector; + +static SELECTORS: SyncLazy>> = SyncLazy::new(|| Mutex::new(Vec::new())); + +#[no_mangle] +pub extern "C" fn sel_getName(sel: *const objc_selector) -> *const c_char { + // 1-indexed, 0 is NULL selector + if let Some(addr) = sel.addr().checked_sub(1) { + let selectors = SELECTORS.lock().unwrap(); + if let Some(&sel) = selectors.get(addr) { + sel.as_ptr() + } else { + // panic!("Unregistered selector") + ptr::null() + } + } else { + // panic!("Null selector") + ptr::null() + } +} + +#[no_mangle] +pub extern "C" fn sel_registerName(name: *const c_char) -> *const objc_selector { + if name.is_null() { + // panic!("Null name") + return ptr::null(); + } + let bytes = unsafe { CStr::from_ptr(name) }; + let mut selectors = SELECTORS.lock().unwrap(); + for (i, &value) in selectors.iter().enumerate() { + if value == bytes { + // 1-indexed, 0 is NULL selector + return ptr::invalid(i + 1); + } + } + + // Not found, create new entry + let sel = Box::leak(Box::from(bytes)); + selectors.push(sel); + ptr::invalid(selectors.len()) +} diff --git a/objc2/src/message/miri.rs b/objc2/src/message/miri.rs new file mode 100644 index 000000000..17ccf4d37 --- /dev/null +++ b/objc2/src/message/miri.rs @@ -0,0 +1,39 @@ +use super::{conditional_try, Encode, MessageArguments, MessageError}; +use crate::ffi; +use crate::runtime::{Class, Object, Sel}; + +pub(crate) unsafe fn send_unverified( + receiver: *mut Object, + sel: Sel, + args: A, +) -> Result +where + A: MessageArguments, + R: Encode, +{ + unsafe { + conditional_try(|| ffi::miri::custom_msg_send(receiver.cast(), sel.as_ptr().cast(), args)) + } +} + +pub(crate) unsafe fn send_super_unverified( + receiver: *mut Object, + superclass: &Class, + sel: Sel, + args: A, +) -> Result +where + A: MessageArguments, + R: Encode, +{ + unsafe { + conditional_try(|| { + ffi::miri::custom_msg_send_super( + receiver.cast(), + superclass.as_ptr(), + sel.as_ptr().cast(), + args, + ) + }) + } +} diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 801e26952..e8869c7ad 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -30,12 +30,15 @@ unsafe fn conditional_try(f: impl FnOnce() -> R) -> Result &str { - let name = unsafe { CStr::from_ptr(ffi::sel_getName(self.ptr)) }; + let ptr = unsafe { ffi::sel_getName(self.ptr) }; + debug_assert!(!ptr.is_null()); + let name = unsafe { CStr::from_ptr(ptr) }; str::from_utf8(name.to_bytes()).unwrap() } @@ -637,6 +639,14 @@ mod tests { assert_eq!(sel.name(), ":"); } + #[test] + fn test_selector_register_repeated() { + let sel1 = Sel::register("repeated:register:"); + let sel2 = Sel::register("repeated:register:"); + assert_eq!(sel1, sel2); + assert_eq!(sel1.as_ptr(), sel2.as_ptr()); + } + #[test] #[should_panic] fn test_sel_register_null() {