RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.

This commit is contained in:
Adam Ierymenko
2022-06-08 07:32:16 -04:00
parent 373ca30269
commit d5ca4e5f52
12611 changed files with 2898014 additions and 284 deletions

View File

@@ -0,0 +1,225 @@
use core::mem;
use crate::convert::slices::WasmSlice;
use crate::convert::RefFromWasmAbi;
use crate::convert::{FromWasmAbi, IntoWasmAbi, ReturnWasmAbi};
use crate::describe::{inform, WasmDescribe, FUNCTION};
use crate::throw_str;
macro_rules! stack_closures {
($( ($cnt:tt $invoke:ident $invoke_mut:ident $($var:ident)*) )*) => ($(
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a (dyn Fn($($var),*) -> R + 'b)
where $($var: FromWasmAbi,)*
R: ReturnWasmAbi
{
type Abi = WasmSlice;
fn into_abi(self) -> WasmSlice {
unsafe {
let (a, b): (usize, usize) = mem::transmute(self);
WasmSlice { ptr: a as u32, len: b as u32 }
}
}
}
#[allow(non_snake_case)]
unsafe extern "C" fn $invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
a: usize,
b: usize,
$($var: <$var as FromWasmAbi>::Abi),*
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// Scope all local variables before we call `return_abi` to
// ensure they're all destroyed as `return_abi` may throw
let ret = {
let f: &dyn Fn($($var),*) -> R = mem::transmute((a, b));
$(
let $var = <$var as FromWasmAbi>::from_abi($var);
)*
f($($var),*)
};
ret.return_abi()
}
impl<'a, $($var,)* R> WasmDescribe for dyn Fn($($var),*) -> R + 'a
where $($var: FromWasmAbi,)*
R: ReturnWasmAbi
{
fn describe() {
inform(FUNCTION);
inform($invoke::<$($var,)* R> as u32);
inform($cnt);
$(<$var as WasmDescribe>::describe();)*
<R as WasmDescribe>::describe();
<R as WasmDescribe>::describe();
}
}
impl<'a, 'b, $($var,)* R> IntoWasmAbi for &'a mut (dyn FnMut($($var),*) -> R + 'b)
where $($var: FromWasmAbi,)*
R: ReturnWasmAbi
{
type Abi = WasmSlice;
fn into_abi(self) -> WasmSlice {
unsafe {
let (a, b): (usize, usize) = mem::transmute(self);
WasmSlice { ptr: a as u32, len: b as u32 }
}
}
}
#[allow(non_snake_case)]
unsafe extern "C" fn $invoke_mut<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
a: usize,
b: usize,
$($var: <$var as FromWasmAbi>::Abi),*
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// Scope all local variables before we call `return_abi` to
// ensure they're all destroyed as `return_abi` may throw
let ret = {
let f: &mut dyn FnMut($($var),*) -> R = mem::transmute((a, b));
$(
let $var = <$var as FromWasmAbi>::from_abi($var);
)*
f($($var),*)
};
ret.return_abi()
}
impl<'a, $($var,)* R> WasmDescribe for dyn FnMut($($var),*) -> R + 'a
where $($var: FromWasmAbi,)*
R: ReturnWasmAbi
{
fn describe() {
inform(FUNCTION);
inform($invoke_mut::<$($var,)* R> as u32);
inform($cnt);
$(<$var as WasmDescribe>::describe();)*
<R as WasmDescribe>::describe();
<R as WasmDescribe>::describe();
}
}
)*)
}
stack_closures! {
(0 invoke0 invoke0_mut)
(1 invoke1 invoke1_mut A)
(2 invoke2 invoke2_mut A B)
(3 invoke3 invoke3_mut A B C)
(4 invoke4 invoke4_mut A B C D)
(5 invoke5 invoke5_mut A B C D E)
(6 invoke6 invoke6_mut A B C D E F)
(7 invoke7 invoke7_mut A B C D E F G)
(8 invoke8 invoke8_mut A B C D E F G H)
}
impl<'a, 'b, A, R> IntoWasmAbi for &'a (dyn Fn(&A) -> R + 'b)
where
A: RefFromWasmAbi,
R: ReturnWasmAbi,
{
type Abi = WasmSlice;
fn into_abi(self) -> WasmSlice {
unsafe {
let (a, b): (usize, usize) = mem::transmute(self);
WasmSlice {
ptr: a as u32,
len: b as u32,
}
}
}
}
#[allow(non_snake_case)]
unsafe extern "C" fn invoke1_ref<A: RefFromWasmAbi, R: ReturnWasmAbi>(
a: usize,
b: usize,
arg: <A as RefFromWasmAbi>::Abi,
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// Scope all local variables before we call `return_abi` to
// ensure they're all destroyed as `return_abi` may throw
let ret = {
let f: &dyn Fn(&A) -> R = mem::transmute((a, b));
let arg = <A as RefFromWasmAbi>::ref_from_abi(arg);
f(&*arg)
};
ret.return_abi()
}
impl<'a, A, R> WasmDescribe for dyn Fn(&A) -> R + 'a
where
A: RefFromWasmAbi,
R: ReturnWasmAbi,
{
fn describe() {
inform(FUNCTION);
inform(invoke1_ref::<A, R> as u32);
inform(1);
<&A as WasmDescribe>::describe();
<R as WasmDescribe>::describe();
<R as WasmDescribe>::describe();
}
}
impl<'a, 'b, A, R> IntoWasmAbi for &'a mut (dyn FnMut(&A) -> R + 'b)
where
A: RefFromWasmAbi,
R: ReturnWasmAbi,
{
type Abi = WasmSlice;
fn into_abi(self) -> WasmSlice {
unsafe {
let (a, b): (usize, usize) = mem::transmute(self);
WasmSlice {
ptr: a as u32,
len: b as u32,
}
}
}
}
#[allow(non_snake_case)]
unsafe extern "C" fn invoke1_mut_ref<A: RefFromWasmAbi, R: ReturnWasmAbi>(
a: usize,
b: usize,
arg: <A as RefFromWasmAbi>::Abi,
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// Scope all local variables before we call `return_abi` to
// ensure they're all destroyed as `return_abi` may throw
let ret = {
let f: &mut dyn FnMut(&A) -> R = mem::transmute((a, b));
let arg = <A as RefFromWasmAbi>::ref_from_abi(arg);
f(&*arg)
};
ret.return_abi()
}
impl<'a, A, R> WasmDescribe for dyn FnMut(&A) -> R + 'a
where
A: RefFromWasmAbi,
R: ReturnWasmAbi,
{
fn describe() {
inform(FUNCTION);
inform(invoke1_mut_ref::<A, R> as u32);
inform(1);
<&A as WasmDescribe>::describe();
<R as WasmDescribe>::describe();
<R as WasmDescribe>::describe();
}
}

View File

@@ -0,0 +1,474 @@
use core::char;
use core::mem::{self, ManuallyDrop};
use crate::convert::traits::WasmAbi;
use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi};
use crate::convert::{OptionFromWasmAbi, OptionIntoWasmAbi, ReturnWasmAbi};
use crate::{Clamped, JsError, JsValue};
unsafe impl WasmAbi for () {}
#[repr(C)]
pub struct WasmOptionalI32 {
pub present: u32,
pub value: i32,
}
unsafe impl WasmAbi for WasmOptionalI32 {}
#[repr(C)]
pub struct WasmOptionalU32 {
pub present: u32,
pub value: u32,
}
unsafe impl WasmAbi for WasmOptionalU32 {}
#[repr(C)]
pub struct WasmOptionalF32 {
pub present: u32,
pub value: f32,
}
unsafe impl WasmAbi for WasmOptionalF32 {}
#[repr(C)]
pub struct WasmOptionalF64 {
pub present: u32,
pub value: f64,
}
unsafe impl WasmAbi for WasmOptionalF64 {}
#[repr(C)]
pub struct Wasm64 {
pub low: u32,
pub high: u32,
}
unsafe impl WasmAbi for Wasm64 {}
#[repr(C)]
pub struct WasmOptional64 {
pub present: u32,
pub low: u32,
pub high: u32,
}
unsafe impl WasmAbi for WasmOptional64 {}
macro_rules! type_wasm_native {
($($t:tt as $c:tt => $r:tt)*) => ($(
impl IntoWasmAbi for $t {
type Abi = $c;
#[inline]
fn into_abi(self) -> $c { self as $c }
}
impl FromWasmAbi for $t {
type Abi = $c;
#[inline]
unsafe fn from_abi(js: $c) -> Self { js as $t }
}
impl IntoWasmAbi for Option<$t> {
type Abi = $r;
#[inline]
fn into_abi(self) -> $r {
match self {
None => $r {
present: 0,
value: 0 as $c,
},
Some(me) => $r {
present: 1,
value: me as $c,
},
}
}
}
impl FromWasmAbi for Option<$t> {
type Abi = $r;
#[inline]
unsafe fn from_abi(js: $r) -> Self {
if js.present == 0 {
None
} else {
Some(js.value as $t)
}
}
}
)*)
}
type_wasm_native!(
i32 as i32 => WasmOptionalI32
isize as i32 => WasmOptionalI32
u32 as u32 => WasmOptionalU32
usize as u32 => WasmOptionalU32
f32 as f32 => WasmOptionalF32
f64 as f64 => WasmOptionalF64
);
macro_rules! type_abi_as_u32 {
($($t:tt)*) => ($(
impl IntoWasmAbi for $t {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 { self as u32 }
}
impl FromWasmAbi for $t {
type Abi = u32;
#[inline]
unsafe fn from_abi(js: u32) -> Self { js as $t }
}
impl OptionIntoWasmAbi for $t {
#[inline]
fn none() -> u32 { 0x00FF_FFFFu32 }
}
impl OptionFromWasmAbi for $t {
#[inline]
fn is_none(js: &u32) -> bool { *js == 0x00FF_FFFFu32 }
}
)*)
}
type_abi_as_u32!(i8 u8 i16 u16);
macro_rules! type_64 {
($($t:tt)*) => ($(
impl IntoWasmAbi for $t {
type Abi = Wasm64;
#[inline]
fn into_abi(self) -> Wasm64 {
Wasm64 {
low: self as u32,
high: (self >> 32) as u32,
}
}
}
impl FromWasmAbi for $t {
type Abi = Wasm64;
#[inline]
unsafe fn from_abi(js: Wasm64) -> $t {
$t::from(js.low) | ($t::from(js.high) << 32)
}
}
impl IntoWasmAbi for Option<$t> {
type Abi = WasmOptional64;
#[inline]
fn into_abi(self) -> WasmOptional64 {
match self {
None => WasmOptional64 {
present: 0,
low: 0 as u32,
high: 0 as u32,
},
Some(me) => WasmOptional64 {
present: 1,
low: me as u32,
high: (me >> 32) as u32,
},
}
}
}
impl FromWasmAbi for Option<$t> {
type Abi = WasmOptional64;
#[inline]
unsafe fn from_abi(js: WasmOptional64) -> Self {
if js.present == 0 {
None
} else {
Some($t::from(js.low) | ($t::from(js.high) << 32))
}
}
}
)*)
}
type_64!(i64 u64);
impl IntoWasmAbi for bool {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 {
self as u32
}
}
impl FromWasmAbi for bool {
type Abi = u32;
#[inline]
unsafe fn from_abi(js: u32) -> bool {
js != 0
}
}
impl OptionIntoWasmAbi for bool {
#[inline]
fn none() -> u32 {
0x00FF_FFFFu32
}
}
impl OptionFromWasmAbi for bool {
#[inline]
fn is_none(js: &u32) -> bool {
*js == 0x00FF_FFFFu32
}
}
impl IntoWasmAbi for char {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 {
self as u32
}
}
impl FromWasmAbi for char {
type Abi = u32;
#[inline]
unsafe fn from_abi(js: u32) -> char {
char::from_u32_unchecked(js)
}
}
impl OptionIntoWasmAbi for char {
#[inline]
fn none() -> u32 {
0x00FF_FFFFu32
}
}
impl OptionFromWasmAbi for char {
#[inline]
fn is_none(js: &u32) -> bool {
*js == 0x00FF_FFFFu32
}
}
impl<T> IntoWasmAbi for *const T {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 {
self as u32
}
}
impl<T> FromWasmAbi for *const T {
type Abi = u32;
#[inline]
unsafe fn from_abi(js: u32) -> *const T {
js as *const T
}
}
impl<T> IntoWasmAbi for *mut T {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 {
self as u32
}
}
impl<T> FromWasmAbi for *mut T {
type Abi = u32;
#[inline]
unsafe fn from_abi(js: u32) -> *mut T {
js as *mut T
}
}
impl IntoWasmAbi for JsValue {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 {
let ret = self.idx;
mem::forget(self);
ret
}
}
impl FromWasmAbi for JsValue {
type Abi = u32;
#[inline]
unsafe fn from_abi(js: u32) -> JsValue {
JsValue::_new(js)
}
}
impl<'a> IntoWasmAbi for &'a JsValue {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 {
self.idx
}
}
impl RefFromWasmAbi for JsValue {
type Abi = u32;
type Anchor = ManuallyDrop<JsValue>;
#[inline]
unsafe fn ref_from_abi(js: u32) -> Self::Anchor {
ManuallyDrop::new(JsValue::_new(js))
}
}
impl<T: OptionIntoWasmAbi> IntoWasmAbi for Option<T> {
type Abi = T::Abi;
#[inline]
fn into_abi(self) -> T::Abi {
match self {
None => T::none(),
Some(me) => me.into_abi(),
}
}
}
impl<T: OptionFromWasmAbi> FromWasmAbi for Option<T> {
type Abi = T::Abi;
#[inline]
unsafe fn from_abi(js: T::Abi) -> Self {
if T::is_none(&js) {
None
} else {
Some(T::from_abi(js))
}
}
}
impl<T: IntoWasmAbi> IntoWasmAbi for Clamped<T> {
type Abi = T::Abi;
#[inline]
fn into_abi(self) -> Self::Abi {
self.0.into_abi()
}
}
impl<T: FromWasmAbi> FromWasmAbi for Clamped<T> {
type Abi = T::Abi;
#[inline]
unsafe fn from_abi(js: T::Abi) -> Self {
Clamped(T::from_abi(js))
}
}
impl IntoWasmAbi for () {
type Abi = ();
#[inline]
fn into_abi(self) {
self
}
}
/// This is an encoding of a Result. It can only store things that can be decoded by the JS
/// bindings.
///
/// At the moment, we do not write the exact struct packing layout of everything into the
/// glue/descriptions of datatypes, so T cannot be arbitrary. The current requirements of the
/// struct unpacker (StructUnpacker), which apply to ResultAbi<T> as a whole, are as follows:
///
/// - repr(C), of course
/// - u32/i32/f32/f64 fields at the "leaf fields" of the "field tree"
/// - layout equivalent to a completely flattened repr(C) struct, constructed by an in order
/// traversal of all the leaf fields in it.
///
/// This means that you can't embed struct A(u32, f64) as struct B(u32, A); because the "completely
/// flattened" struct AB(u32, u32, f64) would miss the 4 byte padding that is actually present
/// within B and then as a consequence also miss the 4 byte padding within A that repr(C) inserts.
///
/// The enemy is padding. Padding is only required when there is an `f64` field. So the enemy is
/// `f64` after anything else, particularly anything arbitrary. There is no smaller sized type, so
/// we don't need to worry about 1-byte integers, etc. It's best, therefore, to place your f64s
/// first in your structs, that's why we have `abi` first, although here it doesn't matter as the
/// other two fields total 8 bytes anyway.
///
#[repr(C)]
pub struct ResultAbi<T> {
/// This field is the same size/align as `T`.
abi: ResultAbiUnion<T>,
/// Order of args here is such that we can pop() the possible error first, deal with it and
/// move on. Later fields are popped off the stack first.
err: u32,
is_err: u32,
}
#[repr(C)]
pub union ResultAbiUnion<T> {
// ManuallyDrop is #[repr(transparent)]
ok: std::mem::ManuallyDrop<T>,
err: (),
}
unsafe impl<T: WasmAbi> WasmAbi for ResultAbi<T> {}
unsafe impl<T: WasmAbi> WasmAbi for ResultAbiUnion<T> {}
impl<T: IntoWasmAbi, E: Into<JsValue>> ReturnWasmAbi for Result<T, E> {
type Abi = ResultAbi<T::Abi>;
#[inline]
fn return_abi(self) -> Self::Abi {
match self {
Ok(v) => {
let abi = ResultAbiUnion {
ok: std::mem::ManuallyDrop::new(v.into_abi()),
};
ResultAbi {
abi,
is_err: 0,
err: 0,
}
}
Err(e) => {
let jsval = e.into();
ResultAbi {
abi: ResultAbiUnion { err: () },
is_err: 1,
err: jsval.into_abi(),
}
}
}
}
}
impl IntoWasmAbi for JsError {
type Abi = <JsValue as IntoWasmAbi>::Abi;
fn into_abi(self) -> Self::Abi {
self.value.into_abi()
}
}

View File

@@ -0,0 +1,11 @@
//! This is mostly an internal module, no stability guarantees are provided. Use
//! at your own risk.
mod closures;
mod impls;
mod slices;
mod traits;
pub use self::impls::*;
pub use self::slices::WasmSlice;
pub use self::traits::*;

View File

@@ -0,0 +1,311 @@
#[cfg(feature = "std")]
use std::prelude::v1::*;
use core::slice;
use core::str;
use crate::cast::JsObject;
use crate::convert::OptionIntoWasmAbi;
use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi};
use cfg_if::cfg_if;
if_std! {
use core::mem;
use crate::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) -> WasmSlice {
let ptr = self.as_ptr();
let len = self.len();
mem::forget(self);
WasmSlice {
ptr: ptr.into_abi(),
len: len as u32,
}
}
}
impl OptionIntoWasmAbi for Box<[$t]> {
#[inline]
fn none() -> WasmSlice { null_slice() }
}
impl FromWasmAbi for Box<[$t]> {
type Abi = WasmSlice;
#[inline]
unsafe fn from_abi(js: WasmSlice) -> Self {
let ptr = <*mut $t>::from_abi(js.ptr);
let len = js.len as usize;
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
}
}
impl OptionFromWasmAbi for Box<[$t]> {
#[inline]
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
}
}
impl<'a> IntoWasmAbi for &'a [$t] {
type Abi = WasmSlice;
#[inline]
fn into_abi(self) -> WasmSlice {
WasmSlice {
ptr: self.as_ptr().into_abi(),
len: self.len() as u32,
}
}
}
impl<'a> OptionIntoWasmAbi for &'a [$t] {
#[inline]
fn none() -> WasmSlice { null_slice() }
}
impl<'a> IntoWasmAbi for &'a mut [$t] {
type Abi = WasmSlice;
#[inline]
fn into_abi(self) -> WasmSlice {
(&*self).into_abi()
}
}
impl<'a> OptionIntoWasmAbi for &'a mut [$t] {
#[inline]
fn none() -> WasmSlice { null_slice() }
}
impl RefFromWasmAbi for [$t] {
type Abi = WasmSlice;
type Anchor = Box<[$t]>;
#[inline]
unsafe fn ref_from_abi(js: WasmSlice) -> Box<[$t]> {
<Box<[$t]>>::from_abi(js)
}
}
impl RefMutFromWasmAbi for [$t] {
type Abi = WasmSlice;
type Anchor = &'static mut [$t];
#[inline]
unsafe fn ref_mut_from_abi(js: WasmSlice)
-> &'static mut [$t]
{
slice::from_raw_parts_mut(
<*mut $t>::from_abi(js.ptr),
js.len as usize,
)
}
}
)*)
}
vectors! {
u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64
}
cfg_if! {
if #[cfg(feature = "enable-interning")] {
#[inline]
fn unsafe_get_cached_str(x: &str) -> Option<WasmSlice> {
// This uses 0 for the ptr as an indication that it is a JsValue and not a str.
crate::cache::intern::unsafe_get_str(x).map(|x| WasmSlice { ptr: 0, len: x })
}
} else {
#[inline]
fn unsafe_get_cached_str(_x: &str) -> Option<WasmSlice> {
None
}
}
}
if_std! {
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
#[inline]
fn into_abi(self) -> Self::Abi {
self.into_boxed_slice().into_abi()
}
}
impl<T> OptionIntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
#[inline]
fn none() -> WasmSlice { null_slice() }
}
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
#[inline]
unsafe fn from_abi(js: Self::Abi) -> Self {
<Box<[T]>>::from_abi(js).into()
}
}
impl<T> OptionFromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi<Abi = WasmSlice> {
#[inline]
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) -> Self::Abi {
// This is safe because the JsValue is immediately looked up in the heap and
// then returned, so use-after-free cannot occur.
unsafe_get_cached_str(&self).unwrap_or_else(|| self.into_bytes().into_abi())
}
}
impl OptionIntoWasmAbi for String {
#[inline]
fn none() -> Self::Abi { null_slice() }
}
impl FromWasmAbi for String {
type Abi = <Vec<u8> as FromWasmAbi>::Abi;
#[inline]
unsafe fn from_abi(js: Self::Abi) -> Self {
String::from_utf8_unchecked(<Vec<u8>>::from_abi(js))
}
}
impl OptionFromWasmAbi for String {
#[inline]
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) -> Self::Abi {
// This is safe because the JsValue is immediately looked up in the heap and
// then returned, so use-after-free cannot occur.
unsafe_get_cached_str(self).unwrap_or_else(|| self.as_bytes().into_abi())
}
}
impl<'a> OptionIntoWasmAbi for &'a str {
#[inline]
fn none() -> Self::Abi {
null_slice()
}
}
impl RefFromWasmAbi for str {
type Abi = <[u8] as RefFromWasmAbi>::Abi;
type Anchor = Box<str>;
#[inline]
unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
mem::transmute::<Box<[u8]>, Box<str>>(<Box<[u8]>>::from_abi(js))
}
}
if_std! {
use crate::JsValue;
impl IntoWasmAbi for Box<[JsValue]> {
type Abi = WasmSlice;
#[inline]
fn into_abi(self) -> WasmSlice {
let ptr = self.as_ptr();
let len = self.len();
mem::forget(self);
WasmSlice {
ptr: ptr.into_abi(),
len: len as u32,
}
}
}
impl OptionIntoWasmAbi for Box<[JsValue]> {
#[inline]
fn none() -> WasmSlice { null_slice() }
}
impl FromWasmAbi for Box<[JsValue]> {
type Abi = WasmSlice;
#[inline]
unsafe fn from_abi(js: WasmSlice) -> Self {
let ptr = <*mut JsValue>::from_abi(js.ptr);
let len = js.len as usize;
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
}
}
impl OptionFromWasmAbi for Box<[JsValue]> {
#[inline]
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
}
impl<T> IntoWasmAbi for Box<[T]> where T: JsObject {
type Abi = WasmSlice;
#[inline]
fn into_abi(self) -> WasmSlice {
let ptr = self.as_ptr();
let len = self.len();
mem::forget(self);
WasmSlice {
ptr: ptr.into_abi(),
len: len as u32,
}
}
}
impl<T> OptionIntoWasmAbi for Box<[T]> where T: JsObject {
#[inline]
fn none() -> WasmSlice { null_slice() }
}
impl<T> FromWasmAbi for Box<[T]> where T: JsObject {
type Abi = WasmSlice;
#[inline]
unsafe fn from_abi(js: WasmSlice) -> Self {
let ptr = <*mut JsValue>::from_abi(js.ptr);
let len = js.len as usize;
let vec: Vec<T> = Vec::from_raw_parts(ptr, len, len).drain(..).map(|js_value| T::unchecked_from_js(js_value)).collect();
return vec.into_boxed_slice();
}
}
impl<T> OptionFromWasmAbi for Box<[T]> where T: JsObject {
#[inline]
fn is_none(slice: &WasmSlice) -> bool { slice.ptr == 0 }
}
}

View File

@@ -0,0 +1,129 @@
use core::ops::{Deref, DerefMut};
use crate::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) -> 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) -> 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) -> 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) -> 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;
}
/// 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 {}
/// A trait representing how to interepret the return value of a function for
/// the wasm ABI.
///
/// This is very similar to the `IntoWasmAbi` trait and in fact has a blanket
/// implementation for all implementors of the `IntoWasmAbi`. The primary use
/// case of this trait is to enable functions to return `Result`, interpreting
/// an error as "rethrow this to JS"
pub trait ReturnWasmAbi: WasmDescribe {
/// Same as `IntoWasmAbi::Abi`
type Abi: WasmAbi;
/// Same as `IntoWasmAbi::into_abi`, except that it may throw and never
/// return in the case of `Err`.
fn return_abi(self) -> Self::Abi;
}
impl<T: IntoWasmAbi> ReturnWasmAbi for T {
type Abi = T::Abi;
#[inline]
fn return_abi(self) -> Self::Abi {
self.into_abi()
}
}