Bug 1347224 - Part 1: Expose fallible methods on the rust ns[C]String bindings, r=froydnj

This patch adds a series of fallible methods for the rust ns[C]String
bindings, as well as the `set_length` method, which is the same as the
`SetLength` method in C++. `set_length` is marked as unsafe.

The decision was made to make the fallible methods seperate from the
infallible methods, and to use seperate Rust->C++ bindings for each of
them, rather than only binding the fallible bindings, and unwrapping
them in rust-land. This is to try to match the C++ API as closely as
possible, and to ensure that the behavior matches.

MozReview-Commit-ID: FkSomkFUFGD
This commit is contained in:
Michael Layzell 2017-03-20 14:40:25 -04:00
Родитель 218f799b87
Коммит 2be5900b6b
3 изменённых файлов: 289 добавлений и 68 удалений

Просмотреть файл

@ -117,6 +117,7 @@
use std::ops::{Deref, DerefMut};
use std::marker::PhantomData;
use std::borrow;
use std::slice;
use std::ptr;
use std::mem;
@ -150,11 +151,20 @@ const F_CLASS_FIXED: u32 = 1 << 16; // indicates that |this| is of type nsTFixed
macro_rules! define_string_types {
{
char_t = $char_t: ty;
AString = $AString: ident;
String = $String: ident;
FixedString = $FixedString: ident;
StringLike = $StringLike: ident;
StringAdapter = $StringAdapter: ident;
StringRepr = $StringRepr: ident;
drop = $drop: ident;
assign = $assign: ident, $fallible_assign: ident;
append = $append: ident, $fallible_append: ident;
set_length = $set_length: ident, $fallible_set_length: ident;
} => {
/// The representation of a ns[C]String type in C++. This type is
/// used internally by our definition of ns[C]String to ensure layout
@ -208,6 +218,72 @@ macro_rules! define_string_types {
_prohibit_constructor: [u8; 0],
}
impl $AString {
/// Assign the value of `other` into self, overwriting any value
/// currently stored. Performs an optimized assignment when possible
/// if `other` is a `nsA[C]String`.
pub fn assign<T: $StringLike + ?Sized>(&mut self, other: &T) {
unsafe { $assign(self, other.adapt().as_ptr()) };
}
/// Assign the value of `other` into self, overwriting any value
/// currently stored. Performs an optimized assignment when possible
/// if `other` is a `nsA[C]String`.
///
/// Returns Ok(()) on success, and Err(()) if the allocation failed.
pub fn fallible_assign<T: $StringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
if unsafe { $fallible_assign(self, other.adapt().as_ptr()) } {
Ok(())
} else {
Err(())
}
}
/// Append the value of `other` into self.
pub fn append<T: $StringLike + ?Sized>(&mut self, other: &T) {
unsafe { $append(self, other.adapt().as_ptr()) };
}
/// Append the value of `other` into self.
///
/// Returns Ok(()) on success, and Err(()) if the allocation failed.
pub fn fallible_append<T: $StringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
if unsafe { $fallible_append(self, other.adapt().as_ptr()) } {
Ok(())
} else {
Err(())
}
}
/// Set the length of the string to the passed-in length, and expand
/// the backing capacity to match. This method is unsafe as it can
/// expose uninitialized memory when len is greater than the current
/// length of the string.
pub unsafe fn set_length(&mut self, len: u32) {
$set_length(self, len);
}
/// Set the length of the string to the passed-in length, and expand
/// the backing capacity to match. This method is unsafe as it can
/// expose uninitialized memory when len is greater than the current
/// length of the string.
///
/// Returns Ok(()) on success, and Err(()) if the allocation failed.
pub unsafe fn fallible_set_length(&mut self, len: u32) -> Result<(), ()> {
if $fallible_set_length(self, len) {
Ok(())
} else {
Err(())
}
}
pub fn truncate(&mut self) {
unsafe {
self.set_length(0);
}
}
}
impl Deref for $AString {
type Target = [$char_t];
fn deref(&self) -> &[$char_t] {
@ -277,6 +353,14 @@ macro_rules! define_string_types {
}
}
impl<'a> Drop for $String<'a> {
fn drop(&mut self) {
unsafe {
$drop(&mut **self);
}
}
}
impl<'a> Deref for $String<'a> {
type Target = $AString;
fn deref(&self) -> &$AString {
@ -511,6 +595,93 @@ macro_rules! define_string_types {
$AString::eq(self, *other)
}
}
/// An adapter type to allow for passing both types which coerce to
/// &[$char_type], and &$AString to a function, while still performing
/// optimized operations when passed the $AString.
pub enum $StringAdapter<'a> {
Borrowed($String<'a>),
Abstract(&'a $AString),
}
impl<'a> $StringAdapter<'a> {
fn as_ptr(&self) -> *const $AString {
&**self
}
}
impl<'a> Deref for $StringAdapter<'a> {
type Target = $AString;
fn deref(&self) -> &$AString {
match *self {
$StringAdapter::Borrowed(ref s) => s,
$StringAdapter::Abstract(ref s) => s,
}
}
}
/// This trait is implemented on types which are `ns[C]String`-like, in
/// that they can at very low cost be converted to a borrowed
/// `&nsA[C]String`. Unfortunately, the intermediate type
/// `ns[C]StringAdapter` is required as well due to types like `&[u8]`
/// needing to be (cheaply) wrapped in a `nsCString` on the stack to
/// create the `&nsACString`.
///
/// This trait is used to DWIM when calling the methods on
/// `nsA[C]String`.
pub trait $StringLike {
fn adapt(&self) -> $StringAdapter;
}
impl<'a, T: $StringLike + ?Sized> $StringLike for &'a T {
fn adapt(&self) -> $StringAdapter {
<T as $StringLike>::adapt(*self)
}
}
impl<'a, T> $StringLike for borrow::Cow<'a, T>
where T: $StringLike + borrow::ToOwned + ?Sized {
fn adapt(&self) -> $StringAdapter {
<T as $StringLike>::adapt(self.as_ref())
}
}
impl $StringLike for $AString {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Abstract(self)
}
}
impl<'a> $StringLike for $String<'a> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Abstract(self)
}
}
impl<'a> $StringLike for $FixedString<'a> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Abstract(self)
}
}
impl $StringLike for [$char_t] {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Borrowed($String::from(self))
}
}
impl $StringLike for Vec<$char_t> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Borrowed($String::from(&self[..]))
}
}
impl $StringLike for Box<[$char_t]> {
fn adapt(&self) -> $StringAdapter {
$StringAdapter::Borrowed($String::from(&self[..]))
}
}
}
}
@ -525,53 +696,45 @@ define_string_types! {
String = nsCString;
FixedString = nsFixedCString;
StringLike = nsCStringLike;
StringAdapter = nsCStringAdapter;
StringRepr = nsCStringRepr;
drop = Gecko_FinalizeCString;
assign = Gecko_AssignCString, Gecko_FallibleAssignCString;
append = Gecko_AppendCString, Gecko_FallibleAppendCString;
set_length = Gecko_SetLengthCString, Gecko_FallibleSetLengthCString;
}
impl nsACString {
pub fn assign<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
let s = nsCString::from(other.as_ref());
unsafe {
Gecko_AssignCString(self, &*s);
}
}
pub fn assign_utf16<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
self.assign(&[]);
pub fn assign_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) {
self.truncate();
self.append_utf16(other);
}
pub fn append<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
let s = nsCString::from(other.as_ref());
pub fn fallible_assign_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
self.truncate();
self.fallible_append_utf16(other)
}
pub fn append_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) {
unsafe {
Gecko_AppendCString(self, &*s);
Gecko_AppendUTF16toCString(self, other.adapt().as_ptr());
}
}
pub fn append_utf16<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
let s = nsString::from(other.as_ref());
unsafe {
Gecko_AppendUTF16toCString(self, &*s);
pub fn fallible_append_utf16<T: nsStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
if unsafe { Gecko_FallibleAppendUTF16toCString(self, other.adapt().as_ptr()) } {
Ok(())
} else {
Err(())
}
}
pub unsafe fn as_str_unchecked(&self) -> &str {
str::from_utf8_unchecked(self)
}
pub fn truncate(&mut self) {
unsafe {
Gecko_TruncateCString(self);
}
}
}
impl<'a> Drop for nsCString<'a> {
fn drop(&mut self) {
unsafe {
Gecko_FinalizeCString(&mut **self);
}
}
}
impl<'a> From<&'a str> for nsCString<'a> {
@ -618,6 +781,24 @@ impl cmp::PartialEq<str> for nsACString {
}
}
impl nsCStringLike for str {
fn adapt(&self) -> nsCStringAdapter {
nsCStringAdapter::Borrowed(nsCString::from(self))
}
}
impl nsCStringLike for String {
fn adapt(&self) -> nsCStringAdapter {
nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
}
}
impl nsCStringLike for Box<str> {
fn adapt(&self) -> nsCStringAdapter {
nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
}
}
#[macro_export]
macro_rules! ns_auto_cstring {
($name:ident) => {
@ -637,47 +818,39 @@ define_string_types! {
String = nsString;
FixedString = nsFixedString;
StringLike = nsStringLike;
StringAdapter = nsStringAdapter;
StringRepr = nsStringRepr;
drop = Gecko_FinalizeString;
assign = Gecko_AssignString, Gecko_FallibleAssignString;
append = Gecko_AppendString, Gecko_FallibleAppendString;
set_length = Gecko_SetLengthString, Gecko_FallibleSetLengthString;
}
impl nsAString {
pub fn assign<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
let s = nsString::from(other.as_ref());
unsafe {
Gecko_AssignString(self, &*s);
}
}
pub fn assign_utf8<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
self.assign(&[]);
pub fn assign_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
self.truncate();
self.append_utf8(other);
}
pub fn append<T: AsRef<[u16]> + ?Sized>(&mut self, other: &T) {
let s = nsString::from(other.as_ref());
pub fn fallible_assign_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
self.truncate();
self.fallible_append_utf8(other)
}
pub fn append_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) {
unsafe {
Gecko_AppendString(self, &*s);
Gecko_AppendUTF8toString(self, other.adapt().as_ptr());
}
}
pub fn append_utf8<T: AsRef<[u8]> + ?Sized>(&mut self, other: &T) {
let s = nsCString::from(other.as_ref());
unsafe {
Gecko_AppendUTF8toString(self, &*s);
}
}
pub fn truncate(&mut self) {
unsafe {
Gecko_TruncateString(self);
}
}
}
impl<'a> Drop for nsString<'a> {
fn drop(&mut self) {
unsafe {
Gecko_FinalizeString(&mut **self);
pub fn fallible_append_utf8<T: nsCStringLike + ?Sized>(&mut self, other: &T) -> Result<(), ()> {
if unsafe { Gecko_FallibleAppendUTF8toString(self, other.adapt().as_ptr()) } {
Ok(())
} else {
Err(())
}
}
}
@ -730,26 +903,34 @@ macro_rules! ns_auto_string {
#[allow(non_snake_case)]
unsafe fn Gecko_IncrementStringAdoptCount(_: *mut c_void) {}
// NOTE: These bindings currently only expose infallible operations. Perhaps
// consider allowing for fallible methods?
extern "C" {
#[cfg(debug_assertions)]
fn Gecko_IncrementStringAdoptCount(data: *mut c_void);
// Gecko implementation in nsSubstring.cpp
fn Gecko_FinalizeCString(this: *mut nsACString);
fn Gecko_AssignCString(this: *mut nsACString, other: *const nsACString);
fn Gecko_AppendCString(this: *mut nsACString, other: *const nsACString);
fn Gecko_TruncateCString(this: *mut nsACString);
fn Gecko_SetLengthCString(this: *mut nsACString, length: u32);
fn Gecko_FallibleAssignCString(this: *mut nsACString, other: *const nsACString) -> bool;
fn Gecko_FallibleAppendCString(this: *mut nsACString, other: *const nsACString) -> bool;
fn Gecko_FallibleSetLengthCString(this: *mut nsACString, length: u32) -> bool;
fn Gecko_FinalizeString(this: *mut nsAString);
fn Gecko_AssignString(this: *mut nsAString, other: *const nsAString);
fn Gecko_AppendString(this: *mut nsAString, other: *const nsAString);
fn Gecko_TruncateString(this: *mut nsAString);
fn Gecko_SetLengthString(this: *mut nsAString, length: u32);
fn Gecko_FallibleAssignString(this: *mut nsAString, other: *const nsAString) -> bool;
fn Gecko_FallibleAppendString(this: *mut nsAString, other: *const nsAString) -> bool;
fn Gecko_FallibleSetLengthString(this: *mut nsAString, length: u32) -> bool;
// Gecko implementation in nsReadableUtils.cpp
fn Gecko_AppendUTF16toCString(this: *mut nsACString, other: *const nsAString);
fn Gecko_AppendUTF8toString(this: *mut nsAString, other: *const nsACString);
fn Gecko_FallibleAppendUTF16toCString(this: *mut nsACString, other: *const nsAString) -> bool;
fn Gecko_FallibleAppendUTF8toString(this: *mut nsAString, other: *const nsACString) -> bool;
}
//////////////////////////////////////

Просмотреть файл

@ -1380,4 +1380,14 @@ void Gecko_AppendUTF8toString(nsAString* aThis, const nsACString* aOther)
AppendUTF8toUTF16(*aOther, *aThis);
}
bool Gecko_FallibleAppendUTF16toCString(nsACString* aThis, const nsAString* aOther)
{
return AppendUTF16toUTF8(*aOther, *aThis, mozilla::fallible);
}
bool Gecko_FallibleAppendUTF8toString(nsAString* aThis, const nsACString* aOther)
{
return AppendUTF8toUTF16(*aOther, *aThis, mozilla::fallible);
}
}

Просмотреть файл

@ -370,9 +370,24 @@ void Gecko_AppendCString(nsACString* aThis, const nsACString* aOther)
aThis->Append(*aOther);
}
void Gecko_TruncateCString(nsACString* aThis)
void Gecko_SetLengthCString(nsACString* aThis, uint32_t aLength)
{
aThis->Truncate();
aThis->SetLength(aLength);
}
bool Gecko_FallibleAssignCString(nsACString* aThis, const nsACString* aOther)
{
return aThis->Assign(*aOther, mozilla::fallible);
}
bool Gecko_FallibleAppendCString(nsACString* aThis, const nsACString* aOther)
{
return aThis->Append(*aOther, mozilla::fallible);
}
bool Gecko_FallibleSetLengthCString(nsACString* aThis, uint32_t aLength)
{
return aThis->SetLength(aLength, mozilla::fallible);
}
void Gecko_FinalizeString(nsAString* aThis)
@ -390,9 +405,24 @@ void Gecko_AppendString(nsAString* aThis, const nsAString* aOther)
aThis->Append(*aOther);
}
void Gecko_TruncateString(nsAString* aThis)
void Gecko_SetLengthString(nsAString* aThis, uint32_t aLength)
{
aThis->Truncate();
aThis->SetLength(aLength);
}
bool Gecko_FallibleAssignString(nsAString* aThis, const nsAString* aOther)
{
return aThis->Assign(*aOther, mozilla::fallible);
}
bool Gecko_FallibleAppendString(nsAString* aThis, const nsAString* aOther)
{
return aThis->Append(*aOther, mozilla::fallible);
}
bool Gecko_FallibleSetLengthString(nsAString* aThis, uint32_t aLength)
{
return aThis->SetLength(aLength, mozilla::fallible);
}
} // extern "C"