1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 use nserror::{nsresult, NS_ERROR_NULL_POINTER}; 6 7 /// The xpcom_method macro generates a Rust XPCOM method stub that converts 8 /// raw pointer arguments to references, calls a Rustic implementation 9 /// of the method, writes its return value into the XPCOM method's outparameter, 10 /// and returns an nsresult. 11 /// 12 /// In other words, given an XPCOM method like: 13 /// 14 /// ```ignore 15 /// interface nsIFooBarBaz : nsISupports { 16 /// nsIVariant foo(in AUTF8String bar, [optional] in bool baz); 17 /// } 18 /// ``` 19 /// 20 /// And a Rust implementation that uses #[derive(xpcom)] to implement it: 21 /// 22 /// ```ignore 23 /// #[derive(xpcom)] 24 /// #[xpimplements(nsIFooBarBaz)] 25 /// #[refcnt = "atomic"] 26 /// struct InitFooBarBaz { 27 /// // … 28 /// } 29 /// ``` 30 /// 31 /// With the appropriate extern crate and use declarations 32 /// 33 /// ```ignore 34 /// extern crate xpcom; 35 /// use xpcom::xpcom_method; 36 /// ``` 37 /// 38 /// Invoking the macro with the name of the XPCOM method, the name of its 39 /// Rustic implementation, the set of its arguments, and its return value: 40 /// 41 /// ```ignore 42 /// impl FooBarBaz { 43 /// xpcom_method!( 44 /// foo => Foo(bar: *const nsACString, baz: bool) -> *const nsIVariant 45 /// ); 46 /// } 47 /// ``` 48 /// 49 /// Results in the macro generating an XPCOM stub like the following: 50 /// 51 /// ```ignore 52 /// unsafe fn Foo(&self, bar: *const nsACString, baz: bool, retval: *mut *const nsIVariant) -> nsresult { 53 /// let bar = match Ensure::ensure(bar) { 54 /// Ok(val) => val, 55 /// Err(result) => return result, 56 /// }; 57 /// let baz = match Ensure::ensure(baz) { 58 /// Ok(val) => val, 59 /// Err(result) => return result, 60 /// }; 61 /// 62 /// match self.foo(bar, baz) { 63 /// Ok(val) => { 64 /// val.forget(&mut *retval); 65 /// NS_OK 66 /// } 67 /// Err(error) => { 68 /// error!("{}", error); 69 /// error.into() 70 /// } 71 /// } 72 /// } 73 /// ``` 74 /// 75 /// Which calls a Rustic implementation (that you implement) like the following: 76 /// 77 /// ```ignore 78 /// impl FooBarBaz { 79 /// fn foo(&self, bar: &nsACString, baz: bool) -> Result<RefPtr<nsIVariant>, nsresult> { 80 /// // … 81 /// } 82 /// } 83 /// ``` 84 /// 85 /// Notes: 86 /// 87 /// On error, the Rustic implementation can return an Err(nsresult) or any 88 /// other type that implements Into<nsresult>. So you can define and return 89 /// a custom error type, which the XPCOM stub will convert to nsresult. 90 /// 91 /// This macro assumes that all non-null pointer arguments are valid! 92 /// It does ensure that they aren't null, using the `ensure_param` macro. 93 /// But it doesn't otherwise check their validity. That makes the function 94 /// unsafe, so callers must ensure that they only call it with valid pointer 95 /// arguments. 96 #[macro_export] 97 macro_rules! xpcom_method { 98 // This rule is provided to ensure external modules don't need to import 99 // internal implementation details of xpcom_method. 100 // The @ensure_param rule converts raw pointer arguments to references, 101 // returning NS_ERROR_NULL_POINTER if the argument is_null(). 102 // 103 // Notes: 104 // 105 // This rule can be called on a non-pointer copy parameter, but there's no 106 // benefit to doing so. The macro will just set the value of the parameter 107 // to itself. (This macro does this anyway due to limitations in declarative 108 // macros; it isn't currently possible to distinguish between pointer and 109 // copy types when processing a set of parameters.) 110 // 111 // The macro currently supports only in-parameters (*const nsIFoo); It 112 // doesn't (yet?) support out-parameters (*mut nsIFoo). The xpcom_method 113 // macro itself does, however, support the return value out-parameter. 114 (@ensure_param $name:ident) => { 115 let $name = match $crate::Ensure::ensure($name) { 116 Ok(val) => val, 117 Err(result) => return result, 118 }; 119 }; 120 121 // `#[allow(non_snake_case)]` is used for each method because `$xpcom_name` 122 // is almost always UpperCamelCase, and Rust gives a warning that it should 123 // be snake_case. It isn't reasonable to rename the XPCOM methods, so 124 // silence the warning. 125 126 // A method whose return value is a *mut *const nsISomething type. 127 // Example: foo => Foo(bar: *const nsACString, baz: bool) -> *const nsIVariant 128 ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> *const $retval:ty) => { 129 #[allow(non_snake_case)] 130 unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut *const $retval) -> nsresult { 131 $(xpcom_method!(@ensure_param $param_name);)* 132 match self.$rust_name($($param_name, )*) { 133 Ok(val) => { 134 val.forget(&mut *retval); 135 NS_OK 136 } 137 Err(error) => { 138 error.into() 139 } 140 } 141 } 142 }; 143 144 // A method whose return value is a *mut nsAString type. 145 // Example: foo => Foo(bar: *const nsACString, baz: bool) -> nsAString 146 ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> nsAString) => { 147 #[allow(non_snake_case)] 148 unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut nsAString) -> nsresult { 149 $(xpcom_method!(@ensure_param $param_name);)* 150 match self.$rust_name($($param_name, )*) { 151 Ok(val) => { 152 (*retval).assign(&val); 153 NS_OK 154 } 155 Err(error) => { 156 error.into() 157 } 158 } 159 } 160 }; 161 162 // A method whose return value is a *mut nsACString type. 163 // Example: foo => Foo(bar: *const nsACString, baz: bool) -> nsACString 164 ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> nsACString) => { 165 #[allow(non_snake_case)] 166 unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut nsACString) -> nsresult { 167 $(xpcom_method!(@ensure_param $param_name);)* 168 match self.$rust_name($($param_name, )*) { 169 Ok(val) => { 170 (*retval).assign(&val); 171 NS_OK 172 } 173 Err(error) => { 174 error.into() 175 } 176 } 177 } 178 }; 179 180 // A method whose return value is a non-nsA[C]String *mut type. 181 // Example: foo => Foo(bar: *const nsACString, baz: bool) -> bool 182 ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*) -> $retval:ty) => { 183 #[allow(non_snake_case)] 184 unsafe fn $xpcom_name(&self, $($param_name: $param_type,)* retval: *mut $retval) -> nsresult { 185 $(xpcom_method!(@ensure_param $param_name);)* 186 match self.$rust_name($($param_name, )*) { 187 Ok(val) => { 188 *retval = val; 189 NS_OK 190 } 191 Err(error) => { 192 error.into() 193 } 194 } 195 } 196 }; 197 198 // A method that doesn't have a return value. 199 // Example: foo => Foo(bar: *const nsACString, baz: bool) 200 ($rust_name:ident => $xpcom_name:ident($($param_name:ident: $param_type:ty),*)) => { 201 #[allow(non_snake_case)] 202 unsafe fn $xpcom_name(&self, $($param_name: $param_type,)*) -> nsresult { 203 $(xpcom_method!(@ensure_param $param_name);)* 204 match self.$rust_name($($param_name, )*) { 205 Ok(_) => NS_OK, 206 Err(error) => { 207 error.into() 208 } 209 } 210 } 211 }; 212 } 213 214 /// A trait that ensures that a raw pointer isn't null and converts it to 215 /// a reference. Because of limitations in declarative macros, this includes an 216 /// implementation for types that are Copy, which simply returns the value 217 /// itself. 218 #[doc(hidden)] 219 pub trait Ensure<T> { ensure(value: T) -> Self220 unsafe fn ensure(value: T) -> Self; 221 } 222 223 impl<'a, T: 'a> Ensure<*const T> for Result<&'a T, nsresult> { ensure(ptr: *const T) -> Result<&'a T, nsresult>224 unsafe fn ensure(ptr: *const T) -> Result<&'a T, nsresult> { 225 if ptr.is_null() { 226 Err(NS_ERROR_NULL_POINTER) 227 } else { 228 Ok(&*ptr) 229 } 230 } 231 } 232 233 impl<'a, T: 'a> Ensure<*const T> for Result<Option<&'a T>, nsresult> { ensure(ptr: *const T) -> Result<Option<&'a T>, nsresult>234 unsafe fn ensure(ptr: *const T) -> Result<Option<&'a T>, nsresult> { 235 Ok(if ptr.is_null() { None } else { Some(&*ptr) }) 236 } 237 } 238 239 impl<T: Copy> Ensure<T> for Result<T, nsresult> { ensure(copyable: T) -> Result<T, nsresult>240 unsafe fn ensure(copyable: T) -> Result<T, nsresult> { 241 Ok(copyable) 242 } 243 } 244