Bug 1482608 - Convert null pointers passed to xpcom_methods into Options. r=myk,nika

Differential Revision: https://phabricator.services.mozilla.com/D20075

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Lina Cambridge 2019-03-25 04:49:36 +00:00
parent 35f0c7a7f9
commit 1ffcc4917b
2 changed files with 61 additions and 16 deletions

View File

@ -88,10 +88,10 @@ use nserror::{nsresult, NS_ERROR_NULL_POINTER};
/// a custom error type, which the XPCOM stub will convert to nsresult.
///
/// This macro assumes that all non-null pointer arguments are valid!
/// It does ensure that they aren't null, returning NS_ERROR_NULL_POINTER
/// for null pointers. But it doesn't otherwise check their validity.
/// That makes the function unsafe, so callers must ensure that they only
/// call it with valid pointer arguments.
/// It does ensure that they aren't null, using the `ensure_param` macro.
/// But it doesn't otherwise check their validity. That makes the function
/// unsafe, so callers must ensure that they only call it with valid pointer
/// arguments.
#[macro_export]
macro_rules! xpcom_method {
// `#[allow(non_snake_case)]` is used for each method because `$xpcom_name`
@ -187,10 +187,15 @@ macro_rules! xpcom_method {
};
}
/// The ensure_param macro converts raw pointer arguments to references,
/// returning NS_ERROR_NULL_POINTER if the argument is_null(). It isn't
/// The ensure_param macro converts raw pointer arguments to references, and
/// passes them to a Rustic implementation of an XPCOM method. This macro isn't
/// intended to be used directly but rather via the xpcom_method macro.
///
/// If the argument `is_null()`, and the corresponding Rustic method parameter
/// is `&T`, the macro returns `NS_ERROR_NULL_POINTER`. However, if the
/// parameter is `Option<&T>`, the macro passes `None` instead. This makes it
/// easy to use optional arguments safely.
///
/// Given the appropriate extern crate and use declarations (which include
/// using the Ensure trait from this module):
///
@ -200,22 +205,36 @@ macro_rules! xpcom_method {
/// use xpcom::Ensure;
/// ```
///
/// Invoking the macro with a parameter like `foo: *const nsIBar`:
/// Invoking the macro like this:
///
/// ```ignore
/// ensure_param!(foo: *const nsIBar);
/// fn do_something_with_foo(foo: &nsIBar) {}
/// fn DoSomethingWithFoo(foo: *const nsIBar) -> nsresult {
/// let foo = ensure_param!(foo);
/// do_something_with_foo(foo);
/// }
/// ```
///
/// Results in the macro generating a code block like the following:
/// Expands to code like this:
///
/// ```ignore
/// let foo = match Ensure::ensure(foo) {
/// Ok(val) => val,
/// Err(result) => return result,
/// };
/// fn do_something_with_foo(foo: &nsIBar) {}
/// fn DoSomethingWithFoo(foo: *const nsIBar) -> nsresult {
/// let foo = match Ensure::ensure(foo) {
/// Ok(val) => val,
/// Err(result) => return result,
/// };
/// do_something_with_foo(foo);
/// }
/// ```
///
/// Which converts `foo` from a `*const nsIBar` to a `&nsIBar`.
/// Which converts `foo` from a `*const nsIBar` to a `&nsIBar`, or returns
/// `NS_ERROR_NULL_POINTER` if `foo` is null.
///
/// To pass an optional argument, we only need to change
/// `do_something_with_foo` to take an `Option<&nsIBar> instead. The macro
/// generates the same code, but `do_something_with_foo` receives `None` if
/// `foo` is null.
///
/// Notes:
///
@ -259,6 +278,16 @@ impl<'a, T: 'a> Ensure<*const T> for Result<&'a T, nsresult> {
}
}
impl<'a, T: 'a> Ensure<*const T> for Result<Option<&'a T>, nsresult> {
unsafe fn ensure(ptr: *const T) -> Result<Option<&'a T>, nsresult> {
Ok(if ptr.is_null() {
None
} else {
Some(&*ptr)
})
}
}
impl<T: Copy> Ensure<T> for Result<T, nsresult> {
unsafe fn ensure(copyable: T) -> Result<T, nsresult> {
Ok(copyable)

View File

@ -22,7 +22,7 @@
//!
//! // Implementing methods on an XPCOM Struct
//! impl ImplRunnable {
//! pub fn Run(&self) -> nsresult {
//! unsafe fn Run(&self) -> nsresult {
//! println!("{}", self.i);
//! NS_OK
//! }
@ -110,7 +110,7 @@
//! // implemented manually.
//! impl ImplRunnable {
//! // The method should have the same name as the corresponding C++ method.
//! pub fn Run(&self) -> nsresult {
//! unsafe fn Run(&self) -> nsresult {
//! // Fields defined on the `Init` struct will be directly on the
//! // generated struct.
//! println!("{}", self.i);
@ -119,6 +119,22 @@
//! }
//! ```
//!
//! XPCOM methods implemented in Rust have signatures similar to methods
//! implemented in C++.
//!
//! ```ignore
//! // nsISupports foo(in long long bar, in AString baz);
//! unsafe fn Foo(&self, bar: libc::int64_t, baz: *const nsAString,
//! _retval: *mut *const nsISupports) -> nsresult;
//!
//! // AString qux(in nsISupports ham);
//! unsafe fn Qux(&self, ham: *const nsISupports,
//! _retval: *mut nsAString) -> nsresult;
//! ```
//!
//! This is a little tedious, so the `xpcom_method!` macro provides a convenient
//! way to generate wrappers around more idiomatic Rust methods.
//!
//! [`xpcom`]: ../xpcom/index.html
//! [`nsIRunnable`]: ../xpcom/struct.nsIRunnable.html
//! [`RefCounted`]: ../xpcom/struct.RefCounted.html