diff options
Diffstat (limited to 'third_party/rust/objc_exception')
-rw-r--r-- | third_party/rust/objc_exception/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/objc_exception/Cargo.toml | 25 | ||||
-rw-r--r-- | third_party/rust/objc_exception/build.rs | 7 | ||||
-rw-r--r-- | third_party/rust/objc_exception/extern/exception.m | 21 | ||||
-rw-r--r-- | third_party/rust/objc_exception/src/lib.rs | 100 |
5 files changed, 154 insertions, 0 deletions
diff --git a/third_party/rust/objc_exception/.cargo-checksum.json b/third_party/rust/objc_exception/.cargo-checksum.json new file mode 100644 index 0000000000..ac8ccd308e --- /dev/null +++ b/third_party/rust/objc_exception/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"4c7727c5464280a94b263b3fbabd775838ce86794834b1adab9bfeefc8a2b731","build.rs":"6734724a6f3c46150f5a6a2adf07b9003ae6f1627fa0ee8fcafa28e9d4aafc15","extern/exception.m":"a6ee21d820126e98ee49ac34db1a6770cfd01f0fb0f71d03127e7eeff91a47c5","src/lib.rs":"24f3b04002dbf24397cc4fd2da7045d49c8b3a06101bab1918d007f7ae9b2207"},"package":"ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"}
\ No newline at end of file diff --git a/third_party/rust/objc_exception/Cargo.toml b/third_party/rust/objc_exception/Cargo.toml new file mode 100644 index 0000000000..7cdfacb7f7 --- /dev/null +++ b/third_party/rust/objc_exception/Cargo.toml @@ -0,0 +1,25 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "objc_exception" +version = "0.1.2" +authors = ["Steven Sheldon"] +build = "build.rs" +exclude = [".gitignore"] +description = "Rust interface for Objective-C's throw and try/catch statements." +documentation = "http://ssheldon.github.io/rust-objc/objc_exception/" +keywords = ["objective-c", "osx", "ios"] +license = "MIT" +repository = "http://github.com/SSheldon/rust-objc-exception" +[build-dependencies.cc] +version = "1" diff --git a/third_party/rust/objc_exception/build.rs b/third_party/rust/objc_exception/build.rs new file mode 100644 index 0000000000..ba728b6e16 --- /dev/null +++ b/third_party/rust/objc_exception/build.rs @@ -0,0 +1,7 @@ +extern crate cc; + +fn main() { + cc::Build::new() + .file("extern/exception.m") + .compile("libexception.a"); +} diff --git a/third_party/rust/objc_exception/extern/exception.m b/third_party/rust/objc_exception/extern/exception.m new file mode 100644 index 0000000000..700439ecf7 --- /dev/null +++ b/third_party/rust/objc_exception/extern/exception.m @@ -0,0 +1,21 @@ +#include <objc/objc.h> +#include <objc/NSObject.h> + +void RustObjCExceptionThrow(id exception) { + @throw exception; +} + +int RustObjCExceptionTryCatch(void (*try)(void *), void *context, id *error) { + @try { + try(context); + if (error) { + *error = nil; + } + return 0; + } @catch (id exception) { + if (error) { + *error = [exception retain]; + } + return 1; + } +} diff --git a/third_party/rust/objc_exception/src/lib.rs b/third_party/rust/objc_exception/src/lib.rs new file mode 100644 index 0000000000..d381f60c70 --- /dev/null +++ b/third_party/rust/objc_exception/src/lib.rs @@ -0,0 +1,100 @@ +//! Rust interface for Objective-C's `@throw` and `@try`/`@catch` statements. + +use std::mem; +use std::os::raw::{c_int, c_void}; +use std::ptr; + +#[link(name = "objc", kind = "dylib")] +extern { } + +extern { + fn RustObjCExceptionThrow(exception: *mut c_void); + fn RustObjCExceptionTryCatch(try: extern fn(*mut c_void), + context: *mut c_void, error: *mut *mut c_void) -> c_int; +} + +/// An opaque type representing any Objective-C object thrown as an exception. +pub enum Exception { } + +/// Throws an Objective-C exception. +/// The argument must be a pointer to an Objective-C object. +/// +/// Unsafe because this unwinds from Objective-C. +pub unsafe fn throw(exception: *mut Exception) -> ! { + RustObjCExceptionThrow(exception as *mut _); + unreachable!(); +} + +unsafe fn try_no_ret<F>(closure: F) -> Result<(), *mut Exception> + where F: FnOnce() { + extern fn try_objc_execute_closure<F>(closure: &mut Option<F>) + where F: FnOnce() { + // This is always passed Some, so it's safe to unwrap + let closure = closure.take().unwrap(); + closure(); + } + + let f: extern fn(&mut Option<F>) = try_objc_execute_closure; + let f: extern fn(*mut c_void) = mem::transmute(f); + // Wrap the closure in an Option so it can be taken + let mut closure = Some(closure); + let context = &mut closure as *mut _ as *mut c_void; + + let mut exception = ptr::null_mut(); + let success = RustObjCExceptionTryCatch(f, context, &mut exception); + + if success == 0 { + Ok(()) + } else { + Err(exception as *mut _) + } +} + +/// Tries to execute the given closure and catches an Objective-C exception +/// if one is thrown. +/// +/// Returns a `Result` that is either `Ok` if the closure succeeded without an +/// exception being thrown, or an `Err` with a pointer to an exception if one +/// was thrown. The exception is retained and so must be released. +/// +/// Unsafe because this encourages unwinding through the closure from +/// Objective-C, which is not safe. +pub unsafe fn try<F, R>(closure: F) -> Result<R, *mut Exception> + where F: FnOnce() -> R { + let mut value = None; + let result = { + let value_ref = &mut value; + try_no_ret(move || { + *value_ref = Some(closure()); + }) + }; + // If the try succeeded, this was set so it's safe to unwrap + result.map(|_| value.unwrap()) +} + +#[cfg(test)] +mod tests { + use std::ptr; + use super::{throw, try}; + + #[test] + fn test_try() { + unsafe { + let s = "Hello".to_string(); + let result = try(move || { + if s.len() > 0 { + throw(ptr::null_mut()); + } + s.len() + }); + assert!(result.unwrap_err() == ptr::null_mut()); + + let mut s = "Hello".to_string(); + let result = try(move || { + s.push_str(", World!"); + s + }); + assert!(result.unwrap() == "Hello, World!"); + } + } +} |