summaryrefslogtreecommitdiffstats
path: root/third_party/rust/objc_exception
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/objc_exception')
-rw-r--r--third_party/rust/objc_exception/.cargo-checksum.json1
-rw-r--r--third_party/rust/objc_exception/Cargo.toml25
-rw-r--r--third_party/rust/objc_exception/build.rs7
-rw-r--r--third_party/rust/objc_exception/extern/exception.m21
-rw-r--r--third_party/rust/objc_exception/src/lib.rs100
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!");
+ }
+ }
+}