mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-07-31 20:11:55 +00:00
Add support for optional slice types (#507)
* Shard the `convert.rs` module into sub-modules Hopefully this'll make the organization a little nicer over time! * Start adding support for optional types This commit starts adding support for optional types to wasm-bindgen as arguments/return values to functions. The strategy here is to add two new traits, `OptionIntoWasmAbi` and `OptionFromWasmAbi`. These two traits are used as a blanket impl to implement `IntoWasmAbi` and `FromWasmAbi` for `Option<T>`. Some consequences of this design: * It should be possible to ensure `Option<SomeForeignType>` implements to/from wasm traits. This is because the option-based traits can be implemented for foreign types. * A specialized implementation is possible for all types, so there's no need for `Option<T>` to introduce unnecessary overhead. * Two new traits is a bit unforutnate but I can't currently think of an alternative design that works for the above two constraints, although it doesn't mean one doesn't exist! * The error messages for "can't use this type here" is actually halfway decent because it says these new traits need to be implemented, which provides a good place to document and talk about what's going on here! * Nested references like `Option<&T>` can't implement `FromWasmAbi`. This means that you can't define a function in Rust which takes `Option<&str>`. It may be possible to do this one day but it'll likely require more trait trickery than I'm capable of right now. * Add support for optional slices This commit adds support for optional slice types, things like strings and arrays. The null representation of these has a pointer value of 0, which should never happen in normal Rust. Otherwise the various plumbing is done throughout the tooling to enable these types in all locations. * Fix `takeObject` on global sentinels These don't have a reference count as they're always expected to work, so avoid actually dropping a reference on them. * Remove some no longer needed bindings * Add support for optional anyref types This commit adds support for optional imported class types. Each type imported with `#[wasm_bindgen]` automatically implements the relevant traits and now supports `Option<Foo>` in various argument/return positions. * Fix building without the `std` feature * Actually fix the build... * Add support for optional types to WebIDL Closes #502
This commit is contained in:
146
src/convert/closures.rs
Normal file
146
src/convert/closures.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use core::mem;
|
||||
|
||||
use convert::{FromWasmAbi, IntoWasmAbi, GlobalStack, Stack};
|
||||
use throw;
|
||||
|
||||
macro_rules! stack_closures {
|
||||
($( ($($var:ident)*) )*) => ($(
|
||||
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (Fn($($var),*) -> R + 'b)
|
||||
where $($var: FromWasmAbi,)*
|
||||
R: IntoWasmAbi
|
||||
{
|
||||
type Abi = u32;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) -> <R as IntoWasmAbi>::Abi {
|
||||
if a == 0 {
|
||||
throw("closure invoked recursively or destroyed already");
|
||||
}
|
||||
let f: &Fn($($var),*) -> R = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
||||
}
|
||||
unsafe {
|
||||
let (a, b): (usize, usize) = mem::transmute(self);
|
||||
extra.push(a as u32);
|
||||
extra.push(b as u32);
|
||||
invoke::<$($var,)* R> as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, $($var,)*> IntoWasmAbi for &'a (Fn($($var),*) + 'b)
|
||||
where $($var: FromWasmAbi,)*
|
||||
{
|
||||
type Abi = u32;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* >(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) {
|
||||
if a == 0 {
|
||||
throw("closure invoked recursively or destroyed already");
|
||||
}
|
||||
let f: &Fn($($var),*) = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*)
|
||||
}
|
||||
unsafe {
|
||||
let (a, b): (usize, usize) = mem::transmute(self);
|
||||
extra.push(a as u32);
|
||||
extra.push(b as u32);
|
||||
invoke::<$($var,)*> as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a mut (FnMut($($var),*) -> R + 'b)
|
||||
where $($var: FromWasmAbi,)*
|
||||
R: IntoWasmAbi
|
||||
{
|
||||
type Abi = u32;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* R: IntoWasmAbi>(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) -> <R as IntoWasmAbi>::Abi {
|
||||
if a == 0 {
|
||||
throw("closure invoked recursively or destroyed already");
|
||||
}
|
||||
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*).into_abi(&mut GlobalStack::new())
|
||||
}
|
||||
unsafe {
|
||||
let (a, b): (usize, usize) = mem::transmute(self);
|
||||
extra.push(a as u32);
|
||||
extra.push(b as u32);
|
||||
invoke::<$($var,)* R> as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, $($var,)*> IntoWasmAbi for &'a mut (FnMut($($var),*) + 'b)
|
||||
where $($var: FromWasmAbi,)*
|
||||
{
|
||||
type Abi = u32;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> u32 {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe extern fn invoke<$($var: FromWasmAbi,)* >(
|
||||
a: usize,
|
||||
b: usize,
|
||||
$($var: <$var as FromWasmAbi>::Abi),*
|
||||
) {
|
||||
if a == 0 {
|
||||
throw("closure invoked recursively or destroyed already");
|
||||
}
|
||||
let f: &mut FnMut($($var),*) = mem::transmute((a, b));
|
||||
let mut _stack = GlobalStack::new();
|
||||
$(
|
||||
let $var = <$var as FromWasmAbi>::from_abi($var, &mut _stack);
|
||||
)*
|
||||
f($($var),*)
|
||||
}
|
||||
unsafe {
|
||||
let (a, b): (usize, usize) = mem::transmute(self);
|
||||
extra.push(a as u32);
|
||||
extra.push(b as u32);
|
||||
invoke::<$($var,)*> as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
stack_closures! {
|
||||
()
|
||||
(A)
|
||||
(A B)
|
||||
(A B C)
|
||||
(A B C D)
|
||||
(A B C D E)
|
||||
(A B C D E F)
|
||||
(A B C D E F G)
|
||||
}
|
||||
|
204
src/convert/impls.rs
Normal file
204
src/convert/impls.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use core::char;
|
||||
use core::mem::{self, ManuallyDrop};
|
||||
|
||||
use convert::slices::WasmSlice;
|
||||
use convert::{Stack, FromWasmAbi, IntoWasmAbi, RefFromWasmAbi};
|
||||
use convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
|
||||
use JsValue;
|
||||
|
||||
macro_rules! simple {
|
||||
($($t:tt)*) => ($(
|
||||
impl IntoWasmAbi for $t {
|
||||
type Abi = $t;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _extra: &mut Stack) -> $t { self }
|
||||
}
|
||||
|
||||
impl FromWasmAbi for $t {
|
||||
type Abi = $t;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js }
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
simple!(u32 i32 f32 f64);
|
||||
|
||||
macro_rules! sixtyfour {
|
||||
($($t:tt)*) => ($(
|
||||
impl IntoWasmAbi for $t {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _extra: &mut Stack) -> WasmSlice {
|
||||
WasmSlice {
|
||||
ptr: self as u32,
|
||||
len: (self >> 32) as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasmAbi for $t {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: WasmSlice, _extra: &mut Stack) -> $t {
|
||||
(js.ptr as $t) | ((js.len as $t) << 32)
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
sixtyfour!(i64 u64);
|
||||
|
||||
macro_rules! as_u32 {
|
||||
($($t:tt)*) => ($(
|
||||
impl IntoWasmAbi for $t {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
|
||||
}
|
||||
|
||||
impl FromWasmAbi for $t {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t }
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
as_u32!(i8 u8 i16 u16 isize usize);
|
||||
|
||||
impl IntoWasmAbi for bool {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasmAbi for bool {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool {
|
||||
js != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoWasmAbi for char {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasmAbi for char {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> char {
|
||||
char::from_u32_unchecked(js)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoWasmAbi for *const T {
|
||||
type Abi = u32;
|
||||
|
||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromWasmAbi for *const T {
|
||||
type Abi = u32;
|
||||
|
||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *const T {
|
||||
js as *const T
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoWasmAbi for *mut T {
|
||||
type Abi = u32;
|
||||
|
||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromWasmAbi for *mut T {
|
||||
type Abi = u32;
|
||||
|
||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *mut T {
|
||||
js as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoWasmAbi for JsValue {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||
let ret = self.idx;
|
||||
mem::forget(self);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasmAbi for JsValue {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue {
|
||||
JsValue { idx: js }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoWasmAbi for &'a JsValue {
|
||||
type Abi = u32;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, _extra: &mut Stack) -> u32 {
|
||||
self.idx
|
||||
}
|
||||
}
|
||||
|
||||
impl RefFromWasmAbi for JsValue {
|
||||
type Abi = u32;
|
||||
type Anchor = ManuallyDrop<JsValue>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_from_abi(js: u32, _extra: &mut Stack) -> Self::Anchor {
|
||||
ManuallyDrop::new(JsValue { idx: js })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: OptionIntoWasmAbi> IntoWasmAbi for Option<T> {
|
||||
type Abi = T::Abi;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> T::Abi {
|
||||
match self {
|
||||
Some(me) => me.into_abi(extra),
|
||||
None => T::none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
|
||||
type Abi = T::Abi;
|
||||
|
||||
unsafe fn from_abi(js: T::Abi, extra: &mut Stack) -> Self {
|
||||
if T::is_none(&js) {
|
||||
None
|
||||
} else {
|
||||
Some(T::from_abi(js, extra))
|
||||
}
|
||||
}
|
||||
}
|
36
src/convert/mod.rs
Normal file
36
src/convert/mod.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
//! This is mostly an internal module, no stability guarantees are provided. Use
|
||||
//! at your own risk.
|
||||
|
||||
mod traits;
|
||||
mod impls;
|
||||
mod slices;
|
||||
mod closures;
|
||||
|
||||
pub use self::slices::WasmSlice;
|
||||
pub use self::traits::*;
|
||||
|
||||
pub struct GlobalStack {
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl GlobalStack {
|
||||
#[inline]
|
||||
pub unsafe fn new() -> GlobalStack {
|
||||
GlobalStack { next: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Stack for GlobalStack {
|
||||
#[inline]
|
||||
fn push(&mut self, val: u32) {
|
||||
use __rt::{
|
||||
__wbindgen_global_argument_ptr as global_ptr,
|
||||
GLOBAL_STACK_CAP,
|
||||
};
|
||||
unsafe {
|
||||
assert!(self.next < GLOBAL_STACK_CAP);
|
||||
*global_ptr().offset(self.next as isize) = val;
|
||||
self.next += 1;
|
||||
}
|
||||
}
|
||||
}
|
240
src/convert/slices.rs
Normal file
240
src/convert/slices.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
#[cfg(feature = "std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
use core::slice;
|
||||
use core::str;
|
||||
|
||||
use convert::{WasmAbi, IntoWasmAbi, FromWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi};
|
||||
use convert::{Stack, OptionIntoWasmAbi};
|
||||
|
||||
if_std! {
|
||||
use core::mem;
|
||||
use convert::OptionFromWasmAbi;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct WasmSlice {
|
||||
pub ptr: u32,
|
||||
pub len: u32,
|
||||
}
|
||||
|
||||
unsafe impl WasmAbi for WasmSlice {}
|
||||
|
||||
#[inline]
|
||||
fn null_slice() -> WasmSlice {
|
||||
WasmSlice { ptr: 0, len: 0 }
|
||||
}
|
||||
|
||||
macro_rules! vectors {
|
||||
($($t:ident)*) => ($(
|
||||
if_std! {
|
||||
impl IntoWasmAbi for Box<[$t]> {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.len();
|
||||
mem::forget(self);
|
||||
WasmSlice {
|
||||
ptr: ptr.into_abi(extra),
|
||||
len: len as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionIntoWasmAbi for Box<[$t]> {
|
||||
fn none() -> WasmSlice { null_slice() }
|
||||
}
|
||||
|
||||
impl FromWasmAbi for Box<[$t]> {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self {
|
||||
let ptr = <*mut $t>::from_abi(js.ptr, extra);
|
||||
let len = js.len as usize;
|
||||
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionFromWasmAbi for Box<[$t]> {
|
||||
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoWasmAbi for &'a [$t] {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||
WasmSlice {
|
||||
ptr: self.as_ptr().into_abi(extra),
|
||||
len: self.len() as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OptionIntoWasmAbi for &'a [$t] {
|
||||
fn none() -> WasmSlice { null_slice() }
|
||||
}
|
||||
|
||||
impl<'a> IntoWasmAbi for &'a mut [$t] {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||
(&*self).into_abi(extra)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
|
||||
fn none() -> WasmSlice { null_slice() }
|
||||
}
|
||||
|
||||
impl RefFromWasmAbi for [$t] {
|
||||
type Abi = WasmSlice;
|
||||
type Anchor = &'static [$t];
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_from_abi(js: WasmSlice, extra: &mut Stack) -> &'static [$t] {
|
||||
slice::from_raw_parts(
|
||||
<*const $t>::from_abi(js.ptr, extra),
|
||||
js.len as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RefMutFromWasmAbi for [$t] {
|
||||
type Abi = WasmSlice;
|
||||
type Anchor = &'static mut [$t];
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_mut_from_abi(js: WasmSlice, extra: &mut Stack)
|
||||
-> &'static mut [$t]
|
||||
{
|
||||
slice::from_raw_parts_mut(
|
||||
<*mut $t>::from_abi(js.ptr, extra),
|
||||
js.len as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
)*)
|
||||
}
|
||||
|
||||
vectors! {
|
||||
u8 i8 u16 i16 u32 i32 u64 i64 f32 f64
|
||||
}
|
||||
|
||||
if_std! {
|
||||
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
||||
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
|
||||
|
||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||
self.into_boxed_slice().into_abi(extra)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OptionIntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
||||
fn none() -> WasmSlice { null_slice() }
|
||||
}
|
||||
|
||||
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
||||
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
|
||||
|
||||
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
|
||||
<Box<[T]>>::from_abi(js, extra).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OptionFromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
|
||||
fn is_none(abi: &WasmSlice) -> bool { abi.ptr == 0 }
|
||||
}
|
||||
|
||||
impl IntoWasmAbi for String {
|
||||
type Abi = <Vec<u8> as IntoWasmAbi>::Abi;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||
self.into_bytes().into_abi(extra)
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionIntoWasmAbi for String {
|
||||
fn none() -> WasmSlice { null_slice() }
|
||||
}
|
||||
|
||||
impl FromWasmAbi for String {
|
||||
type Abi = <Vec<u8> as FromWasmAbi>::Abi;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
|
||||
String::from_utf8_unchecked(<Vec<u8>>::from_abi(js, extra))
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionFromWasmAbi for String {
|
||||
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoWasmAbi for &'a str {
|
||||
type Abi = <&'a [u8] as IntoWasmAbi>::Abi;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
|
||||
self.as_bytes().into_abi(extra)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OptionIntoWasmAbi for &'a str {
|
||||
fn none() -> WasmSlice { null_slice() }
|
||||
}
|
||||
|
||||
impl RefFromWasmAbi for str {
|
||||
type Abi = <[u8] as RefFromWasmAbi>::Abi;
|
||||
type Anchor = &'static str;
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
|
||||
str::from_utf8_unchecked(<[u8]>::ref_from_abi(js, extra))
|
||||
}
|
||||
}
|
||||
|
||||
if_std! {
|
||||
use JsValue;
|
||||
|
||||
impl IntoWasmAbi for Box<[JsValue]> {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
fn into_abi(self, extra: &mut Stack) -> WasmSlice {
|
||||
let ptr = self.as_ptr();
|
||||
let len = self.len();
|
||||
mem::forget(self);
|
||||
WasmSlice {
|
||||
ptr: ptr.into_abi(extra),
|
||||
len: len as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionIntoWasmAbi for Box<[JsValue]> {
|
||||
fn none() -> WasmSlice { null_slice() }
|
||||
}
|
||||
|
||||
impl FromWasmAbi for Box<[JsValue]> {
|
||||
type Abi = WasmSlice;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_abi(js: WasmSlice, extra: &mut Stack) -> Self {
|
||||
let ptr = <*mut JsValue>::from_abi(js.ptr, extra);
|
||||
let len = js.len as usize;
|
||||
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionFromWasmAbi for Box<[JsValue]> {
|
||||
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
|
||||
}
|
||||
}
|
108
src/convert/traits.rs
Normal file
108
src/convert/traits.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
use describe::*;
|
||||
|
||||
/// A trait for anything that can be converted into a type that can cross the
|
||||
/// wasm ABI directly, eg `u32` or `f64`.
|
||||
///
|
||||
/// This is the opposite operation as `FromWasmAbi` and `Ref[Mut]FromWasmAbi`.
|
||||
pub trait IntoWasmAbi: WasmDescribe {
|
||||
/// The wasm ABI type that this converts into when crossing the ABI
|
||||
/// boundary.
|
||||
type Abi: WasmAbi;
|
||||
|
||||
/// Convert `self` into `Self::Abi` so that it can be sent across the wasm
|
||||
/// ABI boundary.
|
||||
fn into_abi(self, extra: &mut Stack) -> Self::Abi;
|
||||
}
|
||||
|
||||
/// A trait for anything that can be recovered by-value from the wasm ABI
|
||||
/// boundary, eg a Rust `u8` can be recovered from the wasm ABI `u32` type.
|
||||
///
|
||||
/// This is the by-value variant of the opposite operation as `IntoWasmAbi`.
|
||||
pub trait FromWasmAbi: WasmDescribe {
|
||||
/// The wasm ABI type that this converts from when coming back out from the
|
||||
/// ABI boundary.
|
||||
type Abi: WasmAbi;
|
||||
|
||||
/// Recover a `Self` from `Self::Abi`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This is only safe to call when -- and implementations may assume that --
|
||||
/// the supplied `Self::Abi` was previously generated by a call to `<Self as
|
||||
/// IntoWasmAbi>::into_abi()` or the moral equivalent in JS.
|
||||
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self;
|
||||
}
|
||||
|
||||
/// A trait for anything that can be recovered as some sort of shared reference
|
||||
/// from the wasm ABI boundary.
|
||||
///
|
||||
/// This is the shared reference variant of the opposite operation as
|
||||
/// `IntoWasmAbi`.
|
||||
pub trait RefFromWasmAbi: WasmDescribe {
|
||||
/// The wasm ABI type references to `Self` are recovered from.
|
||||
type Abi: WasmAbi;
|
||||
|
||||
/// The type that holds the reference to `Self` for the duration of the
|
||||
/// invocation of the function that has an `&Self` parameter. This is
|
||||
/// required to ensure that the lifetimes don't persist beyond one function
|
||||
/// call, and so that they remain anonymous.
|
||||
type Anchor: Deref<Target = Self>;
|
||||
|
||||
/// Recover a `Self::Anchor` from `Self::Abi`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Same as `FromWasmAbi::from_abi`.
|
||||
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
||||
}
|
||||
|
||||
/// Dual of the `RefFromWasmAbi` trait, except for mutable references.
|
||||
pub trait RefMutFromWasmAbi: WasmDescribe {
|
||||
/// Same as `RefFromWasmAbi::Abi`
|
||||
type Abi: WasmAbi;
|
||||
/// Same as `RefFromWasmAbi::Anchor`
|
||||
type Anchor: DerefMut<Target = Self>;
|
||||
/// Same as `RefFromWasmAbi::ref_from_abi`
|
||||
unsafe fn ref_mut_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
|
||||
}
|
||||
|
||||
/// Indicates that this type can be passed to JS as `Option<Self>`.
|
||||
///
|
||||
/// This trait is used when implementing `IntoWasmAbi for Option<T>`.
|
||||
pub trait OptionIntoWasmAbi: IntoWasmAbi {
|
||||
/// Returns an ABI instance indicating "none", which JS will interpret as
|
||||
/// the `None` branch of this option.
|
||||
///
|
||||
/// It should be guaranteed that the `IntoWasmAbi` can never produce the ABI
|
||||
/// value returned here.
|
||||
fn none() -> Self::Abi;
|
||||
}
|
||||
|
||||
/// Indicates that this type can be received from JS as `Option<Self>`.
|
||||
///
|
||||
/// This trait is used when implementing `FromWasmAbi for Option<T>`.
|
||||
pub trait OptionFromWasmAbi: FromWasmAbi {
|
||||
/// Tests whether the argument is a "none" instance. If so it will be
|
||||
/// deserialized as `None`, and otherwise it will be passed to
|
||||
/// `FromWasmAbi`.
|
||||
fn is_none(abi: &Self::Abi) -> bool;
|
||||
}
|
||||
|
||||
pub trait Stack {
|
||||
fn push(&mut self, bits: u32);
|
||||
}
|
||||
|
||||
/// An unsafe trait which represents types that are ABI-safe to pass via wasm
|
||||
/// arguments.
|
||||
///
|
||||
/// This is an unsafe trait to implement as there's no guarantee the type is
|
||||
/// actually safe to transfer across the was boundary, it's up to you to
|
||||
/// guarantee this so codegen works correctly.
|
||||
pub unsafe trait WasmAbi {}
|
||||
|
||||
unsafe impl WasmAbi for u32 {}
|
||||
unsafe impl WasmAbi for i32 {}
|
||||
unsafe impl WasmAbi for f32 {}
|
||||
unsafe impl WasmAbi for f64 {}
|
Reference in New Issue
Block a user