Skip to content

Commit a65ce59

Browse files
committed
Refactor uses of objc_msgSend to no longer have clashing definitions
This is very similar to what Apple's own headers encourage you to do (cast the function pointer before use instead of making new declarations). Additionally, I'm documenting a few of the memory management rules we're following, ensuring that the `args` function doesn't leak memory (if you wrap it in an autorelease pool).
1 parent 5526682 commit a65ce59

File tree

1 file changed

+38
-29
lines changed

1 file changed

+38
-29
lines changed

library/std/src/sys/unix/args.rs

+38-29
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,9 @@ mod imp {
201201

202202
// As _NSGetArgc and _NSGetArgv aren't mentioned in iOS docs
203203
// and use underscores in their names - they're most probably
204-
// are considered private and therefore should be avoided
205-
// Here is another way to get arguments using Objective C
206-
// runtime
204+
// are considered private and therefore should be avoided.
205+
// Here is another way to get arguments using the Objective-C
206+
// runtime.
207207
//
208208
// In general it looks like:
209209
// res = Vec::new()
@@ -213,51 +213,60 @@ mod imp {
213213
// res
214214
#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))]
215215
pub fn args() -> Args {
216-
use crate::ffi::OsString;
216+
use crate::ffi::{c_char, c_uchar, c_void, OsString};
217217
use crate::mem;
218218
use crate::str;
219219

220-
extern "C" {
221-
fn sel_registerName(name: *const libc::c_uchar) -> Sel;
222-
fn objc_getClass(class_name: *const libc::c_uchar) -> NsId;
223-
}
220+
type Sel = *const c_void;
221+
type NsId = *const c_void;
222+
type NSUInteger = usize;
224223

225-
#[cfg(target_arch = "aarch64")]
226224
extern "C" {
227-
fn objc_msgSend(obj: NsId, sel: Sel) -> NsId;
228-
#[allow(clashing_extern_declarations)]
229-
#[link_name = "objc_msgSend"]
230-
fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId;
231-
}
225+
fn sel_registerName(name: *const c_uchar) -> Sel;
226+
fn objc_getClass(class_name: *const c_uchar) -> NsId;
232227

233-
#[cfg(not(target_arch = "aarch64"))]
234-
extern "C" {
235-
fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
236-
#[allow(clashing_extern_declarations)]
237-
#[link_name = "objc_msgSend"]
238-
fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId;
228+
// This must be transmuted to an appropriate function pointer type before being called.
229+
fn objc_msgSend();
239230
}
240231

241-
type Sel = *const libc::c_void;
242-
type NsId = *const libc::c_void;
232+
const MSG_SEND_PTR: unsafe extern "C" fn() = objc_msgSend;
233+
const MSG_SEND_NO_ARGUMENTS_RETURN_PTR: unsafe extern "C" fn(NsId, Sel) -> *const c_void =
234+
unsafe { mem::transmute(MSG_SEND_PTR) };
235+
const MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER: unsafe extern "C" fn(
236+
NsId,
237+
Sel,
238+
) -> NSUInteger = unsafe { mem::transmute(MSG_SEND_PTR) };
239+
const MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR: unsafe extern "C" fn(
240+
NsId,
241+
Sel,
242+
NSUInteger,
243+
)
244+
-> *const c_void = unsafe { mem::transmute(MSG_SEND_PTR) };
243245

244246
let mut res = Vec::new();
245247

246248
unsafe {
247249
let process_info_sel = sel_registerName("processInfo\0".as_ptr());
248250
let arguments_sel = sel_registerName("arguments\0".as_ptr());
249-
let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
250251
let count_sel = sel_registerName("count\0".as_ptr());
251-
let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
252+
let object_at_index_sel = sel_registerName("objectAtIndex:\0".as_ptr());
253+
let utf8string_sel = sel_registerName("UTF8String\0".as_ptr());
252254

253255
let klass = objc_getClass("NSProcessInfo\0".as_ptr());
254-
let info = objc_msgSend(klass, process_info_sel);
255-
let args = objc_msgSend(info, arguments_sel);
256+
// `+[NSProcessInfo processInfo]` returns an object with +0 retain count, so no need to manually `release`.
257+
let info = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(klass, process_info_sel);
258+
259+
// `-[NSProcessInfo arguments]` returns an object with +0 retain count, so no need to manually `release`.
260+
let args = MSG_SEND_NO_ARGUMENTS_RETURN_PTR(info, arguments_sel);
256261

257-
let cnt: usize = mem::transmute(objc_msgSend(args, count_sel));
262+
let cnt = MSG_SEND_NO_ARGUMENTS_RETURN_NSUINTEGER(args, count_sel);
258263
for i in 0..cnt {
259-
let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong);
260-
let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel));
264+
// `-[NSArray objectAtIndex:]` returns an object whose lifetime is tied to the array, so no need to manually `release`.
265+
let ns_string =
266+
MSG_SEND_NSINTEGER_ARGUMENT_RETURN_PTR(args, object_at_index_sel, i);
267+
// The lifetime of this pointer is tied to the NSString, as well as the current autorelease pool, which is why we heap-allocate the string below.
268+
let utf_c_str: *const c_char =
269+
MSG_SEND_NO_ARGUMENTS_RETURN_PTR(ns_string, utf8string_sel).cast();
261270
let bytes = CStr::from_ptr(utf_c_str).to_bytes();
262271
res.push(OsString::from(str::from_utf8(bytes).unwrap()))
263272
}

0 commit comments

Comments
 (0)