Skip to content
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

Miri support #146

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions objc-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ winobjc = ["gnustep-1-8"]
# TODO
objfw = []

unstable-custom-runtime = []

[package.metadata.docs.rs]
default-target = "x86_64-apple-darwin"

Expand Down
1 change: 1 addition & 0 deletions objc-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
7 changes: 7 additions & 0 deletions objc-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
45 changes: 45 additions & 0 deletions objc-sys/src/miri/class.rs
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 31 additions & 0 deletions objc-sys/src/miri/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use core::mem;

use crate::{objc_class, objc_object, objc_selector};

pub fn custom_msg_send<T, R>(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<T, R>(
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!()
}
9 changes: 9 additions & 0 deletions objc-sys/src/miri/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
49 changes: 49 additions & 0 deletions objc-sys/src/miri/selector.rs
Original file line number Diff line number Diff line change
@@ -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<Mutex<Vec<&'static CStr>>> = 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())
}
39 changes: 39 additions & 0 deletions objc2/src/message/miri.rs
Original file line number Diff line number Diff line change
@@ -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<A, R>(
receiver: *mut Object,
sel: Sel,
args: A,
) -> Result<R, MessageError>
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<A, R>(
receiver: *mut Object,
superclass: &Class,
sel: Sel,
args: A,
) -> Result<R, MessageError>
where
A: MessageArguments,
R: Encode,
{
unsafe {
conditional_try(|| {
ffi::miri::custom_msg_send_super(
receiver.cast(),
superclass.as_ptr(),
sel.as_ptr().cast(),
args,
)
})
}
}
7 changes: 5 additions & 2 deletions objc2/src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ unsafe fn conditional_try<R: Encode>(f: impl FnOnce() -> R) -> Result<R, Message
#[cfg(feature = "malloc")]
mod verify;

#[cfg(apple)]
#[cfg(all(not(miri), apple))]
#[path = "apple/mod.rs"]
mod platform;
#[cfg(gnustep)]
#[cfg(all(not(miri), gnustep))]
#[path = "gnustep.rs"]
mod platform;
#[cfg(miri)]
#[path = "miri.rs"]
mod platform;

use self::platform::{send_super_unverified, send_unverified};
#[cfg(feature = "malloc")]
Expand Down
12 changes: 11 additions & 1 deletion objc2/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ impl Sel {

/// Returns the name of the method specified by self.
pub fn name(&self) -> &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()
}

Expand Down Expand Up @@ -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() {
Expand Down