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