Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1,103 @@
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "enable-interning")] {
use std::thread_local;
use std::string::String;
use std::borrow::ToOwned;
use std::cell::RefCell;
use std::collections::HashMap;
use crate::JsValue;
struct Cache {
entries: RefCell<HashMap<String, JsValue>>,
}
thread_local! {
static CACHE: Cache = Cache {
entries: RefCell::new(HashMap::new()),
};
}
/// This returns the raw index of the cached JsValue, so you must take care
/// so that you don't use it after it is freed.
pub(crate) fn unsafe_get_str(s: &str) -> Option<u32> {
CACHE.with(|cache| {
let cache = cache.entries.borrow();
cache.get(s).map(|x| x.idx)
})
}
fn intern_str(key: &str) {
CACHE.with(|cache| {
let mut cache = cache.entries.borrow_mut();
// Can't use `entry` because `entry` requires a `String`
if !cache.contains_key(key) {
cache.insert(key.to_owned(), JsValue::from(key));
}
})
}
fn unintern_str(key: &str) {
CACHE.with(|cache| {
let mut cache = cache.entries.borrow_mut();
cache.remove(key);
})
}
}
}
/// Interns Rust strings so that it's much faster to send them to JS.
///
/// Sending strings from Rust to JS is slow, because it has to do a full `O(n)`
/// copy and *also* encode from UTF-8 to UTF-16. This must be done every single
/// time a string is sent to JS.
///
/// If you are sending the same string multiple times, you can call this `intern`
/// function, which simply returns its argument unchanged:
///
/// ```rust
/// # use wasm_bindgen::intern;
/// intern("foo") // returns "foo"
/// # ;
/// ```
///
/// However, if you enable the `"enable-interning"` feature for wasm-bindgen,
/// then it will add the string into an internal cache.
///
/// When you send that cached string to JS, it will look it up in the cache,
/// which completely avoids the `O(n)` copy and encoding. This has a significant
/// speed boost (as high as 783%)!
///
/// However, there is a small cost to this caching, so you shouldn't cache every
/// string. Only cache strings which have a high likelihood of being sent
/// to JS multiple times.
///
/// Also, keep in mind that this function is a *performance hint*: it's not
/// *guaranteed* that the string will be cached, and the caching strategy
/// might change at any time, so don't rely upon it.
#[inline]
pub fn intern(s: &str) -> &str {
#[cfg(feature = "enable-interning")]
intern_str(s);
s
}
/// Removes a Rust string from the intern cache.
///
/// This does the opposite of the [`intern`](fn.intern.html) function.
///
/// If the [`intern`](fn.intern.html) function is called again then it will re-intern the string.
#[allow(unused_variables)]
#[inline]
pub fn unintern(s: &str) {
#[cfg(feature = "enable-interning")]
unintern_str(s);
}

View file

@ -0,0 +1 @@
pub mod intern;

View file

@ -0,0 +1,160 @@
use crate::{describe::WasmDescribe, JsValue};
/// A trait for checked and unchecked casting between JS types.
///
/// Specified [in an RFC][rfc] this trait is intended to provide support for
/// casting JS values between different types of one another. In JS there aren't
/// many static types but we've ascribed JS values with static types in Rust,
/// yet they often need to be switched to other types temporarily! This trait
/// provides both checked and unchecked casting into various kinds of values.
///
/// This trait is automatically implemented for any type imported in a
/// `#[wasm_bindgen]` `extern` block.
///
/// [rfc]: https://github.com/rustwasm/rfcs/blob/master/text/002-wasm-bindgen-inheritance-casting.md
pub trait JsCast
where
Self: AsRef<JsValue> + Into<JsValue>,
{
/// Test whether this JS value has a type `T`.
///
/// This method will dynamically check to see if this JS object can be
/// casted to the JS object of type `T`. Usually this uses the `instanceof`
/// operator. This also works with primitive types like
/// booleans/strings/numbers as well as cross-realm object like `Array`
/// which can originate from other iframes.
///
/// In general this is intended to be a more robust version of
/// `is_instance_of`, but if you want strictly the `instanceof` operator
/// it's recommended to use that instead.
fn has_type<T>(&self) -> bool
where
T: JsCast,
{
T::is_type_of(self.as_ref())
}
/// Performs a dynamic cast (checked at runtime) of this value into the
/// target type `T`.
///
/// This method will return `Err(self)` if `self.has_type::<T>()`
/// returns `false`, and otherwise it will return `Ok(T)` manufactured with
/// an unchecked cast (verified correct via the `has_type` operation).
fn dyn_into<T>(self) -> Result<T, Self>
where
T: JsCast,
{
if self.has_type::<T>() {
Ok(self.unchecked_into())
} else {
Err(self)
}
}
/// Performs a dynamic cast (checked at runtime) of this value into the
/// target type `T`.
///
/// This method will return `None` if `self.has_type::<T>()`
/// returns `false`, and otherwise it will return `Some(&T)` manufactured
/// with an unchecked cast (verified correct via the `has_type` operation).
fn dyn_ref<T>(&self) -> Option<&T>
where
T: JsCast,
{
if self.has_type::<T>() {
Some(self.unchecked_ref())
} else {
None
}
}
/// Performs a zero-cost unchecked cast into the specified type.
///
/// This method will convert the `self` value to the type `T`, where both
/// `self` and `T` are simple wrappers around `JsValue`. This method **does
/// not check whether `self` is an instance of `T`**. If used incorrectly
/// then this method may cause runtime exceptions in both Rust and JS, this
/// should be used with caution.
fn unchecked_into<T>(self) -> T
where
T: JsCast,
{
T::unchecked_from_js(self.into())
}
/// Performs a zero-cost unchecked cast into a reference to the specified
/// type.
///
/// This method will convert the `self` value to the type `T`, where both
/// `self` and `T` are simple wrappers around `JsValue`. This method **does
/// not check whether `self` is an instance of `T`**. If used incorrectly
/// then this method may cause runtime exceptions in both Rust and JS, this
/// should be used with caution.
///
/// This method, unlike `unchecked_into`, does not consume ownership of
/// `self` and instead works over a shared reference.
fn unchecked_ref<T>(&self) -> &T
where
T: JsCast,
{
T::unchecked_from_js_ref(self.as_ref())
}
/// Test whether this JS value is an instance of the type `T`.
///
/// This method performs a dynamic check (at runtime) using the JS
/// `instanceof` operator. This method returns `self instanceof T`.
///
/// Note that `instanceof` does not always work with primitive values or
/// across different realms (e.g. iframes). If you're not sure whether you
/// specifically need only `instanceof` it's recommended to use `has_type`
/// instead.
fn is_instance_of<T>(&self) -> bool
where
T: JsCast,
{
T::instanceof(self.as_ref())
}
/// Performs a dynamic `instanceof` check to see whether the `JsValue`
/// provided is an instance of this type.
///
/// This is intended to be an internal implementation detail, you likely
/// won't need to call this. It's generally called through the
/// `is_instance_of` method instead.
fn instanceof(val: &JsValue) -> bool;
/// Performs a dynamic check to see whether the `JsValue` provided
/// is a value of this type.
///
/// Unlike `instanceof`, this can be specialised to use a custom check by
/// adding a `#[wasm_bindgen(is_type_of = callback)]` attribute to the
/// type import declaration.
///
/// Other than that, this is intended to be an internal implementation
/// detail of `has_type` and you likely won't need to call this.
fn is_type_of(val: &JsValue) -> bool {
Self::instanceof(val)
}
/// Performs a zero-cost unchecked conversion from a `JsValue` into an
/// instance of `Self`
///
/// This is intended to be an internal implementation detail, you likely
/// won't need to call this.
fn unchecked_from_js(val: JsValue) -> Self;
/// Performs a zero-cost unchecked conversion from a `&JsValue` into an
/// instance of `&Self`.
///
/// Note the safety of this method, which basically means that `Self` must
/// be a newtype wrapper around `JsValue`.
///
/// This is intended to be an internal implementation detail, you likely
/// won't need to call this.
fn unchecked_from_js_ref(val: &JsValue) -> &Self;
}
/// Trait implemented for wrappers around `JsValue`s generated by `#[wasm_bindgen]`.
#[doc(hidden)]
pub trait JsObject: JsCast + WasmDescribe {}

View file

@ -0,0 +1,899 @@
//! Support for long-lived closures in `wasm-bindgen`
//!
//! This module defines the `Closure` type which is used to pass "owned
//! closures" from Rust to JS. Some more details can be found on the `Closure`
//! type itself.
#![allow(clippy::fn_to_numeric_cast)]
use std::fmt;
use std::mem::{self, ManuallyDrop};
use std::prelude::v1::*;
use crate::convert::*;
use crate::describe::*;
use crate::throw_str;
use crate::JsValue;
use crate::UnwrapThrowExt;
/// A handle to both a closure in Rust as well as JS closure which will invoke
/// the Rust closure.
///
/// A `Closure` is the primary way that a `'static` lifetime closure is
/// transferred from Rust to JS. `Closure` currently requires that the closures
/// it's created with have the `'static` lifetime in Rust for soundness reasons.
///
/// This type is a "handle" in the sense that whenever it is dropped it will
/// invalidate the JS closure that it refers to. Any usage of the closure in JS
/// after the `Closure` has been dropped will raise an exception. It's then up
/// to you to arrange for `Closure` to be properly deallocate at an appropriate
/// location in your program.
///
/// The type parameter on `Closure` is the type of closure that this represents.
/// Currently this can only be the `Fn` and `FnMut` traits with up to 7
/// arguments (and an optional return value).
///
/// # Examples
///
/// Here are a number of examples of using `Closure`.
///
/// ## Using the `setInterval` API
///
/// Sample usage of `Closure` to invoke the `setInterval` API.
///
/// ```rust,no_run
/// use wasm_bindgen::prelude::*;
///
/// #[wasm_bindgen]
/// extern "C" {
/// fn setInterval(closure: &Closure<dyn FnMut()>, time: u32) -> i32;
/// fn clearInterval(id: i32);
///
/// #[wasm_bindgen(js_namespace = console)]
/// fn log(s: &str);
/// }
///
/// #[wasm_bindgen]
/// pub struct IntervalHandle {
/// interval_id: i32,
/// _closure: Closure<dyn FnMut()>,
/// }
///
/// impl Drop for IntervalHandle {
/// fn drop(&mut self) {
/// clearInterval(self.interval_id);
/// }
/// }
///
/// #[wasm_bindgen]
/// pub fn run() -> IntervalHandle {
/// // First up we use `Closure::new` to wrap up a Rust closure and create
/// // a JS closure.
/// let cb = Closure::new(|| {
/// log("interval elapsed!");
/// });
///
/// // Next we pass this via reference to the `setInterval` function, and
/// // `setInterval` gets a handle to the corresponding JS closure.
/// let interval_id = setInterval(&cb, 1_000);
///
/// // If we were to drop `cb` here it would cause an exception to be raised
/// // whenever the interval elapses. Instead we *return* our handle back to JS
/// // so JS can decide when to cancel the interval and deallocate the closure.
/// IntervalHandle {
/// interval_id,
/// _closure: cb,
/// }
/// }
/// ```
///
/// ## Casting a `Closure` to a `js_sys::Function`
///
/// This is the same `setInterval` example as above, except it is using
/// `web_sys` (which uses `js_sys::Function` for callbacks) instead of manually
/// writing bindings to `setInterval` and other Web APIs.
///
/// ```rust,ignore
/// use wasm_bindgen::JsCast;
///
/// #[wasm_bindgen]
/// pub struct IntervalHandle {
/// interval_id: i32,
/// _closure: Closure<dyn FnMut()>,
/// }
///
/// impl Drop for IntervalHandle {
/// fn drop(&mut self) {
/// let window = web_sys::window().unwrap();
/// window.clear_interval_with_handle(self.interval_id);
/// }
/// }
///
/// #[wasm_bindgen]
/// pub fn run() -> Result<IntervalHandle, JsValue> {
/// let cb = Closure::new(|| {
/// web_sys::console::log_1(&"interval elapsed!".into());
/// });
///
/// let window = web_sys::window().unwrap();
/// let interval_id = window.set_interval_with_callback_and_timeout_and_arguments_0(
/// // Note this method call, which uses `as_ref()` to get a `JsValue`
/// // from our `Closure` which is then converted to a `&Function`
/// // using the `JsCast::unchecked_ref` function.
/// cb.as_ref().unchecked_ref(),
/// 1_000,
/// )?;
///
/// // Same as above.
/// Ok(IntervalHandle {
/// interval_id,
/// _closure: cb,
/// })
/// }
/// ```
///
/// ## Using `FnOnce` and `Closure::once` with `requestAnimationFrame`
///
/// Because `requestAnimationFrame` only calls its callback once, we can use
/// `FnOnce` and `Closure::once` with it.
///
/// ```rust,no_run
/// use wasm_bindgen::prelude::*;
///
/// #[wasm_bindgen]
/// extern "C" {
/// fn requestAnimationFrame(closure: &Closure<dyn FnMut()>) -> u32;
/// fn cancelAnimationFrame(id: u32);
///
/// #[wasm_bindgen(js_namespace = console)]
/// fn log(s: &str);
/// }
///
/// #[wasm_bindgen]
/// pub struct AnimationFrameHandle {
/// animation_id: u32,
/// _closure: Closure<dyn FnMut()>,
/// }
///
/// impl Drop for AnimationFrameHandle {
/// fn drop(&mut self) {
/// cancelAnimationFrame(self.animation_id);
/// }
/// }
///
/// // A type that will log a message when it is dropped.
/// struct LogOnDrop(&'static str);
/// impl Drop for LogOnDrop {
/// fn drop(&mut self) {
/// log(self.0);
/// }
/// }
///
/// #[wasm_bindgen]
/// pub fn run() -> AnimationFrameHandle {
/// // We are using `Closure::once` which takes a `FnOnce`, so the function
/// // can drop and/or move things that it closes over.
/// let fired = LogOnDrop("animation frame fired or canceled");
/// let cb = Closure::once(move || drop(fired));
///
/// // Schedule the animation frame!
/// let animation_id = requestAnimationFrame(&cb);
///
/// // Again, return a handle to JS, so that the closure is not dropped
/// // immediately and JS can decide whether to cancel the animation frame.
/// AnimationFrameHandle {
/// animation_id,
/// _closure: cb,
/// }
/// }
/// ```
///
/// ## Converting `FnOnce`s directly into JavaScript Functions with `Closure::once_into_js`
///
/// If we don't want to allow a `FnOnce` to be eagerly dropped (maybe because we
/// just want it to drop after it is called and don't care about cancellation)
/// then we can use the `Closure::once_into_js` function.
///
/// This is the same `requestAnimationFrame` example as above, but without
/// supporting early cancellation.
///
/// ```
/// use wasm_bindgen::prelude::*;
///
/// #[wasm_bindgen]
/// extern "C" {
/// // We modify the binding to take an untyped `JsValue` since that is what
/// // is returned by `Closure::once_into_js`.
/// //
/// // If we were using the `web_sys` binding for `requestAnimationFrame`,
/// // then the call sites would cast the `JsValue` into a `&js_sys::Function`
/// // using `f.unchecked_ref::<js_sys::Function>()`. See the `web_sys`
/// // example above for details.
/// fn requestAnimationFrame(callback: JsValue);
///
/// #[wasm_bindgen(js_namespace = console)]
/// fn log(s: &str);
/// }
///
/// // A type that will log a message when it is dropped.
/// struct LogOnDrop(&'static str);
/// impl Drop for LogOnDrop {
/// fn drop(&mut self) {
/// log(self.0);
/// }
/// }
///
/// #[wasm_bindgen]
/// pub fn run() {
/// // We are using `Closure::once_into_js` which takes a `FnOnce` and
/// // converts it into a JavaScript function, which is returned as a
/// // `JsValue`.
/// let fired = LogOnDrop("animation frame fired");
/// let cb = Closure::once_into_js(move || drop(fired));
///
/// // Schedule the animation frame!
/// requestAnimationFrame(cb);
///
/// // No need to worry about whether or not we drop a `Closure`
/// // here or return some sort of handle to JS!
/// }
/// ```
pub struct Closure<T: ?Sized> {
js: ManuallyDrop<JsValue>,
data: ManuallyDrop<Box<T>>,
}
union FatPtr<T: ?Sized> {
ptr: *mut T,
fields: (usize, usize),
}
impl<T> Closure<T>
where
T: ?Sized + WasmClosure,
{
/// Creates a new instance of `Closure` from the provided Rust function.
///
/// Note that the closure provided here, `F`, has a few requirements
/// associated with it:
///
/// * It must implement `Fn` or `FnMut` (for `FnOnce` functions see
/// `Closure::once` and `Closure::once_into_js`).
///
/// * It must be `'static`, aka no stack references (use the `move`
/// keyword).
///
/// * It can have at most 7 arguments.
///
/// * Its arguments and return values are all types that can be shared with
/// JS (i.e. have `#[wasm_bindgen]` annotations or are simple numbers,
/// etc.)
pub fn new<F>(t: F) -> Closure<T>
where
F: IntoWasmClosure<T> + 'static,
{
Closure::wrap(Box::new(t).unsize())
}
/// A more direct version of `Closure::new` which creates a `Closure` from
/// a `Box<dyn Fn>`/`Box<dyn FnMut>`, which is how it's kept internally.
pub fn wrap(mut data: Box<T>) -> Closure<T> {
assert_eq!(mem::size_of::<*const T>(), mem::size_of::<FatPtr<T>>());
let (a, b) = unsafe {
FatPtr {
ptr: &mut *data as *mut T,
}
.fields
};
// Here we need to create a `JsValue` with the data and `T::invoke()`
// function pointer. To do that we... take a few unconventional turns.
// In essence what happens here is this:
//
// 1. First up, below we call a function, `breaks_if_inlined`. This
// function, as the name implies, does not work if it's inlined.
// More on that in a moment.
// 2. This function internally calls a special import recognized by the
// `wasm-bindgen` CLI tool, `__wbindgen_describe_closure`. This
// imported symbol is similar to `__wbindgen_describe` in that it's
// not intended to show up in the final binary but it's an
// intermediate state for a `wasm-bindgen` binary.
// 3. The `__wbindgen_describe_closure` import is namely passed a
// descriptor function, monomorphized for each invocation.
//
// Most of this doesn't actually make sense to happen at runtime! The
// real magic happens when `wasm-bindgen` comes along and updates our
// generated code. When `wasm-bindgen` runs it performs a few tasks:
//
// * First, it finds all functions that call
// `__wbindgen_describe_closure`. These are all `breaks_if_inlined`
// defined below as the symbol isn't called anywhere else.
// * Next, `wasm-bindgen` executes the `breaks_if_inlined`
// monomorphized functions, passing it dummy arguments. This will
// execute the function just enough to invoke the special import,
// namely telling us about the function pointer that is the describe
// shim.
// * This knowledge is then used to actually find the descriptor in the
// function table which is then executed to figure out the signature
// of the closure.
// * Finally, and probably most heinously, the call to
// `breaks_if_inlined` is rewritten to call an otherwise globally
// imported function. This globally imported function will generate
// the `JsValue` for this closure specialized for the signature in
// question.
//
// Later on `wasm-gc` will clean up all the dead code and ensure that
// we don't actually call `__wbindgen_describe_closure` at runtime. This
// means we will end up not actually calling `breaks_if_inlined` in the
// final binary, all calls to that function should be pruned.
//
// See crates/cli-support/src/js/closures.rs for a more information
// about what's going on here.
extern "C" fn describe<T: WasmClosure + ?Sized>() {
inform(CLOSURE);
T::describe()
}
#[inline(never)]
unsafe fn breaks_if_inlined<T: WasmClosure + ?Sized>(a: usize, b: usize) -> u32 {
super::__wbindgen_describe_closure(a as u32, b as u32, describe::<T> as u32)
}
let idx = unsafe { breaks_if_inlined::<T>(a, b) };
Closure {
js: ManuallyDrop::new(JsValue::_new(idx)),
data: ManuallyDrop::new(data),
}
}
/// Release memory management of this closure from Rust to the JS GC.
///
/// When a `Closure` is dropped it will release the Rust memory and
/// invalidate the associated JS closure, but this isn't always desired.
/// Some callbacks are alive for the entire duration of the program or for a
/// lifetime dynamically managed by the JS GC. This function can be used
/// to drop this `Closure` while keeping the associated JS function still
/// valid.
///
/// If the platform supports weak references, the Rust memory will be
/// reclaimed when the JS closure is GC'd. If weak references is not
/// supported, this can be dangerous if this function is called many times
/// in an application because the memory leak will overwhelm the page
/// quickly and crash the wasm.
pub fn into_js_value(self) -> JsValue {
let idx = self.js.idx;
mem::forget(self);
JsValue::_new(idx)
}
/// Same as `into_js_value`, but doesn't return a value.
pub fn forget(self) {
drop(self.into_js_value());
}
}
// NB: we use a specific `T` for this `Closure<T>` impl block to avoid every
// call site having to provide an explicit, turbo-fished type like
// `Closure::<dyn FnOnce()>::once(...)`.
impl Closure<dyn FnOnce()> {
/// Create a `Closure` from a function that can only be called once.
///
/// Since we have no way of enforcing that JS cannot attempt to call this
/// `FnOne(A...) -> R` more than once, this produces a `Closure<dyn FnMut(A...)
/// -> R>` that will dynamically throw a JavaScript error if called more
/// than once.
///
/// # Example
///
/// ```rust,no_run
/// use wasm_bindgen::prelude::*;
///
/// // Create an non-`Copy`, owned `String`.
/// let mut s = String::from("Hello");
///
/// // Close over `s`. Since `f` returns `s`, it is `FnOnce` and can only be
/// // called once. If it was called a second time, it wouldn't have any `s`
/// // to work with anymore!
/// let f = move || {
/// s += ", World!";
/// s
/// };
///
/// // Create a `Closure` from `f`. Note that the `Closure`'s type parameter
/// // is `FnMut`, even though `f` is `FnOnce`.
/// let closure: Closure<dyn FnMut() -> String> = Closure::once(f);
/// ```
pub fn once<F, A, R>(fn_once: F) -> Closure<F::FnMut>
where
F: 'static + WasmClosureFnOnce<A, R>,
{
Closure::wrap(fn_once.into_fn_mut())
}
/// Convert a `FnOnce(A...) -> R` into a JavaScript `Function` object.
///
/// If the JavaScript function is invoked more than once, it will throw an
/// exception.
///
/// Unlike `Closure::once`, this does *not* return a `Closure` that can be
/// dropped before the function is invoked to deallocate the closure. The
/// only way the `FnOnce` is deallocated is by calling the JavaScript
/// function. If the JavaScript function is never called then the `FnOnce`
/// and everything it closes over will leak.
///
/// ```rust,ignore
/// use wasm_bindgen::{prelude::*, JsCast};
///
/// let f = Closure::once_into_js(move || {
/// // ...
/// });
///
/// assert!(f.is_instance_of::<js_sys::Function>());
/// ```
pub fn once_into_js<F, A, R>(fn_once: F) -> JsValue
where
F: 'static + WasmClosureFnOnce<A, R>,
{
fn_once.into_js_function()
}
}
/// A trait for converting an `FnOnce(A...) -> R` into a `FnMut(A...) -> R` that
/// will throw if ever called more than once.
#[doc(hidden)]
pub trait WasmClosureFnOnce<A, R>: 'static {
type FnMut: ?Sized + 'static + WasmClosure;
fn into_fn_mut(self) -> Box<Self::FnMut>;
fn into_js_function(self) -> JsValue;
}
impl<T: ?Sized> AsRef<JsValue> for Closure<T> {
fn as_ref(&self) -> &JsValue {
&self.js
}
}
impl<T> WasmDescribe for Closure<T>
where
T: WasmClosure + ?Sized,
{
fn describe() {
inform(EXTERNREF);
}
}
// `Closure` can only be passed by reference to imports.
impl<'a, T> IntoWasmAbi for &'a Closure<T>
where
T: WasmClosure + ?Sized,
{
type Abi = u32;
fn into_abi(self) -> u32 {
(&*self.js).into_abi()
}
}
impl<'a, T> OptionIntoWasmAbi for &'a Closure<T>
where
T: WasmClosure + ?Sized,
{
fn none() -> Self::Abi {
0
}
}
fn _check() {
fn _assert<T: IntoWasmAbi>() {}
_assert::<&Closure<dyn Fn()>>();
_assert::<&Closure<dyn Fn(String)>>();
_assert::<&Closure<dyn Fn() -> String>>();
_assert::<&Closure<dyn FnMut()>>();
_assert::<&Closure<dyn FnMut(String)>>();
_assert::<&Closure<dyn FnMut() -> String>>();
}
impl<T> fmt::Debug for Closure<T>
where
T: ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Closure {{ ... }}")
}
}
impl<T> Drop for Closure<T>
where
T: ?Sized,
{
fn drop(&mut self) {
unsafe {
// this will implicitly drop our strong reference in addition to
// invalidating all future invocations of the closure
if super::__wbindgen_cb_drop(self.js.idx) != 0 {
ManuallyDrop::drop(&mut self.data);
}
}
}
}
/// An internal trait for the `Closure` type.
///
/// This trait is not stable and it's not recommended to use this in bounds or
/// implement yourself.
#[doc(hidden)]
pub unsafe trait WasmClosure {
fn describe();
}
/// An internal trait for the `Closure` type.
///
/// This trait is not stable and it's not recommended to use this in bounds or
/// implement yourself.
#[doc(hidden)]
pub trait IntoWasmClosure<T: ?Sized> {
fn unsize(self: Box<Self>) -> Box<T>;
}
// The memory safety here in these implementations below is a bit tricky. We
// want to be able to drop the `Closure` object from within the invocation of a
// `Closure` for cases like promises. That means that while it's running we
// might drop the `Closure`, but that shouldn't invalidate the environment yet.
//
// Instead what we do is to wrap closures in `Rc` variables. The main `Closure`
// has a strong reference count which keeps the trait object alive. Each
// invocation of a closure then *also* clones this and gets a new reference
// count. When the closure returns it will release the reference count.
//
// This means that if the main `Closure` is dropped while it's being invoked
// then destruction is deferred until execution returns. Otherwise it'll
// deallocate data immediately.
macro_rules! doit {
($(
($($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4:ident)*)
)*) => ($(
unsafe impl<$($var,)* R> WasmClosure for dyn Fn($($var),*) -> R + 'static
where $($var: FromWasmAbi + 'static,)*
R: ReturnWasmAbi + 'static,
{
fn describe() {
#[allow(non_snake_case)]
unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
a: usize,
b: usize,
$(
$arg1: <$var::Abi as WasmAbi>::Prim1,
$arg2: <$var::Abi as WasmAbi>::Prim2,
$arg3: <$var::Abi as WasmAbi>::Prim3,
$arg4: <$var::Abi as WasmAbi>::Prim4,
)*
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked after being dropped");
}
// Make sure all stack variables are converted before we
// convert `ret` as it may throw (for `Result`, for
// example)
let ret = {
let f: *const dyn Fn($($var),*) -> R =
FatPtr { fields: (a, b) }.ptr;
$(
let $var = <$var as FromWasmAbi>::from_abi($var::Abi::join($arg1, $arg2, $arg3, $arg4));
)*
(*f)($($var),*)
};
ret.return_abi().into()
}
inform(invoke::<$($var,)* R> as u32);
unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
a: usize,
b: usize,
) {
// This can be called by the JS glue in erroneous situations
// such as when the closure has already been destroyed. If
// that's the case let's not make things worse by
// segfaulting and/or asserting, so just ignore null
// pointers.
if a == 0 {
return;
}
drop(Box::from_raw(FatPtr::<dyn Fn($($var,)*) -> R> {
fields: (a, b)
}.ptr));
}
inform(destroy::<$($var,)* R> as u32);
<&Self>::describe();
}
}
unsafe impl<$($var,)* R> WasmClosure for dyn FnMut($($var),*) -> R + 'static
where $($var: FromWasmAbi + 'static,)*
R: ReturnWasmAbi + 'static,
{
fn describe() {
#[allow(non_snake_case)]
unsafe extern "C" fn invoke<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
a: usize,
b: usize,
$(
$arg1: <$var::Abi as WasmAbi>::Prim1,
$arg2: <$var::Abi as WasmAbi>::Prim2,
$arg3: <$var::Abi as WasmAbi>::Prim3,
$arg4: <$var::Abi as WasmAbi>::Prim4,
)*
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked recursively or after being dropped");
}
// Make sure all stack variables are converted before we
// convert `ret` as it may throw (for `Result`, for
// example)
let ret = {
let f: *const dyn FnMut($($var),*) -> R =
FatPtr { fields: (a, b) }.ptr;
let f = f as *mut dyn FnMut($($var),*) -> R;
$(
let $var = <$var as FromWasmAbi>::from_abi($var::Abi::join($arg1, $arg2, $arg3, $arg4));
)*
(*f)($($var),*)
};
ret.return_abi().into()
}
inform(invoke::<$($var,)* R> as u32);
unsafe extern fn destroy<$($var: FromWasmAbi,)* R: ReturnWasmAbi>(
a: usize,
b: usize,
) {
// See `Fn()` above for why we simply return
if a == 0 {
return;
}
drop(Box::from_raw(FatPtr::<dyn FnMut($($var,)*) -> R> {
fields: (a, b)
}.ptr));
}
inform(destroy::<$($var,)* R> as u32);
<&mut Self>::describe();
}
}
#[allow(non_snake_case, unused_parens)]
impl<T, $($var,)* R> WasmClosureFnOnce<($($var),*), R> for T
where T: 'static + FnOnce($($var),*) -> R,
$($var: FromWasmAbi + 'static,)*
R: ReturnWasmAbi + 'static
{
type FnMut = dyn FnMut($($var),*) -> R;
fn into_fn_mut(self) -> Box<Self::FnMut> {
let mut me = Some(self);
Box::new(move |$($var),*| {
let me = me.take().expect_throw("FnOnce called more than once");
me($($var),*)
})
}
fn into_js_function(self) -> JsValue {
use std::rc::Rc;
use crate::__rt::WasmRefCell;
let mut me = Some(self);
let rc1 = Rc::new(WasmRefCell::new(None));
let rc2 = rc1.clone();
let closure = Closure::wrap(Box::new(move |$($var),*| {
// Invoke ourself and get the result.
let me = me.take().expect_throw("FnOnce called more than once");
let result = me($($var),*);
// And then drop the `Rc` holding this function's `Closure`
// alive.
debug_assert_eq!(Rc::strong_count(&rc2), 1);
let option_closure = rc2.borrow_mut().take();
debug_assert!(option_closure.is_some());
drop(option_closure);
result
}) as Box<dyn FnMut($($var),*) -> R>);
let js_val = closure.as_ref().clone();
*rc1.borrow_mut() = Some(closure);
debug_assert_eq!(Rc::strong_count(&rc1), 2);
drop(rc1);
js_val
}
}
impl<T, $($var,)* R> IntoWasmClosure<dyn FnMut($($var),*) -> R> for T
where T: 'static + FnMut($($var),*) -> R,
$($var: FromWasmAbi + 'static,)*
R: ReturnWasmAbi + 'static,
{
fn unsize(self: Box<Self>) -> Box<dyn FnMut($($var),*) -> R> { self }
}
impl<T, $($var,)* R> IntoWasmClosure<dyn Fn($($var),*) -> R> for T
where T: 'static + Fn($($var),*) -> R,
$($var: FromWasmAbi + 'static,)*
R: ReturnWasmAbi + 'static,
{
fn unsize(self: Box<Self>) -> Box<dyn Fn($($var),*) -> R> { self }
}
)*)
}
doit! {
()
(A a1 a2 a3 a4)
(A a1 a2 a3 a4 B b1 b2 b3 b4)
(A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
(A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
(A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4)
(A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4)
(A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4)
(A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4 H h1 h2 h3 h4)
}
// Copy the above impls down here for where there's only one argument and it's a
// reference. We could add more impls for more kinds of references, but it
// becomes a combinatorial explosion quickly. Let's see how far we can get with
// just this one! Maybe someone else can figure out voodoo so we don't have to
// duplicate.
unsafe impl<A, R> WasmClosure for dyn Fn(&A) -> R
where
A: RefFromWasmAbi,
R: ReturnWasmAbi + 'static,
{
fn describe() {
#[allow(non_snake_case)]
unsafe extern "C" fn invoke<A: RefFromWasmAbi, R: ReturnWasmAbi>(
a: usize,
b: usize,
arg1: <A::Abi as WasmAbi>::Prim1,
arg2: <A::Abi as WasmAbi>::Prim2,
arg3: <A::Abi as WasmAbi>::Prim3,
arg4: <A::Abi as WasmAbi>::Prim4,
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked after being dropped");
}
// Make sure all stack variables are converted before we
// convert `ret` as it may throw (for `Result`, for
// example)
let ret = {
let f: *const dyn Fn(&A) -> R = FatPtr { fields: (a, b) }.ptr;
let arg = <A as RefFromWasmAbi>::ref_from_abi(A::Abi::join(arg1, arg2, arg3, arg4));
(*f)(&*arg)
};
ret.return_abi().into()
}
inform(invoke::<A, R> as u32);
unsafe extern "C" fn destroy<A: RefFromWasmAbi, R: ReturnWasmAbi>(a: usize, b: usize) {
// See `Fn()` above for why we simply return
if a == 0 {
return;
}
drop(Box::from_raw(
FatPtr::<dyn Fn(&A) -> R> { fields: (a, b) }.ptr,
));
}
inform(destroy::<A, R> as u32);
<&Self>::describe();
}
}
unsafe impl<A, R> WasmClosure for dyn FnMut(&A) -> R
where
A: RefFromWasmAbi,
R: ReturnWasmAbi + 'static,
{
fn describe() {
#[allow(non_snake_case)]
unsafe extern "C" fn invoke<A: RefFromWasmAbi, R: ReturnWasmAbi>(
a: usize,
b: usize,
arg1: <A::Abi as WasmAbi>::Prim1,
arg2: <A::Abi as WasmAbi>::Prim2,
arg3: <A::Abi as WasmAbi>::Prim3,
arg4: <A::Abi as WasmAbi>::Prim4,
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked recursively or after being dropped");
}
// Make sure all stack variables are converted before we
// convert `ret` as it may throw (for `Result`, for
// example)
let ret = {
let f: *const dyn FnMut(&A) -> R = FatPtr { fields: (a, b) }.ptr;
let f = f as *mut dyn FnMut(&A) -> R;
let arg = <A as RefFromWasmAbi>::ref_from_abi(A::Abi::join(arg1, arg2, arg3, arg4));
(*f)(&*arg)
};
ret.return_abi().into()
}
inform(invoke::<A, R> as u32);
unsafe extern "C" fn destroy<A: RefFromWasmAbi, R: ReturnWasmAbi>(a: usize, b: usize) {
// See `Fn()` above for why we simply return
if a == 0 {
return;
}
drop(Box::from_raw(
FatPtr::<dyn FnMut(&A) -> R> { fields: (a, b) }.ptr,
));
}
inform(destroy::<A, R> as u32);
<&mut Self>::describe();
}
}
#[allow(non_snake_case)]
impl<T, A, R> WasmClosureFnOnce<(&A,), R> for T
where
T: 'static + FnOnce(&A) -> R,
A: RefFromWasmAbi + 'static,
R: ReturnWasmAbi + 'static,
{
type FnMut = dyn FnMut(&A) -> R;
fn into_fn_mut(self) -> Box<Self::FnMut> {
let mut me = Some(self);
Box::new(move |arg| {
let me = me.take().expect_throw("FnOnce called more than once");
me(arg)
})
}
fn into_js_function(self) -> JsValue {
use crate::__rt::WasmRefCell;
use std::rc::Rc;
let mut me = Some(self);
let rc1 = Rc::new(WasmRefCell::new(None));
let rc2 = rc1.clone();
let closure = Closure::wrap(Box::new(move |arg: &A| {
// Invoke ourself and get the result.
let me = me.take().expect_throw("FnOnce called more than once");
let result = me(arg);
// And then drop the `Rc` holding this function's `Closure`
// alive.
debug_assert_eq!(Rc::strong_count(&rc2), 1);
let option_closure = rc2.borrow_mut().take();
debug_assert!(option_closure.is_some());
drop(option_closure);
result
}) as Box<dyn FnMut(&A) -> R>);
let js_val = closure.as_ref().clone();
*rc1.borrow_mut() = Some(closure);
debug_assert_eq!(Rc::strong_count(&rc1), 2);
drop(rc1);
js_val
}
}

View file

@ -0,0 +1,243 @@
#![allow(clippy::fn_to_numeric_cast)]
use core::mem;
use crate::convert::slices::WasmSlice;
use crate::convert::RefFromWasmAbi;
use crate::convert::{FromWasmAbi, IntoWasmAbi, ReturnWasmAbi, WasmAbi, WasmRet};
use crate::describe::{inform, WasmDescribe, FUNCTION};
use crate::throw_str;
macro_rules! stack_closures {
($( ($cnt:tt $invoke:ident $invoke_mut:ident $($var:ident $arg1:ident $arg2:ident $arg3:ident $arg4: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,
$(
$arg1: <$var::Abi as WasmAbi>::Prim1,
$arg2: <$var::Abi as WasmAbi>::Prim2,
$arg3: <$var::Abi as WasmAbi>::Prim3,
$arg4: <$var::Abi as WasmAbi>::Prim4,
)*
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked after being dropped");
}
// 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::Abi::join($arg1, $arg2, $arg3, $arg4));
)*
f($($var),*)
};
ret.return_abi().into()
}
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,
$(
$arg1: <$var::Abi as WasmAbi>::Prim1,
$arg2: <$var::Abi as WasmAbi>::Prim2,
$arg3: <$var::Abi as WasmAbi>::Prim3,
$arg4: <$var::Abi as WasmAbi>::Prim4,
)*
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked recursively or after being dropped");
}
// 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::Abi::join($arg1, $arg2, $arg3, $arg4));
)*
f($($var),*)
};
ret.return_abi().into()
}
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 a1 a2 a3 a4)
(2 invoke2 invoke2_mut A a1 a2 a3 a4 B b1 b2 b3 b4)
(3 invoke3 invoke3_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4)
(4 invoke4 invoke4_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4)
(5 invoke5 invoke5_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4)
(6 invoke6 invoke6_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4)
(7 invoke7 invoke7_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4)
(8 invoke8 invoke8_mut A a1 a2 a3 a4 B b1 b2 b3 b4 C c1 c2 c3 c4 D d1 d2 d3 d4 E e1 e2 e3 e4 F f1 f2 f3 f4 G g1 g2 g3 g4 H h1 h2 h3 h4)
}
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,
arg1: <A::Abi as WasmAbi>::Prim1,
arg2: <A::Abi as WasmAbi>::Prim2,
arg3: <A::Abi as WasmAbi>::Prim3,
arg4: <A::Abi as WasmAbi>::Prim4,
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked after being dropped");
}
// 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(A::Abi::join(arg1, arg2, arg3, arg4));
f(&*arg)
};
ret.return_abi().into()
}
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,
arg1: <A::Abi as WasmAbi>::Prim1,
arg2: <A::Abi as WasmAbi>::Prim2,
arg3: <A::Abi as WasmAbi>::Prim3,
arg4: <A::Abi as WasmAbi>::Prim4,
) -> WasmRet<R::Abi> {
if a == 0 {
throw_str("closure invoked recursively or after being dropped");
}
// 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(A::Abi::join(arg1, arg2, arg3, arg4));
f(&*arg)
};
ret.return_abi().into()
}
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,508 @@
use core::char;
use core::mem::{self, ManuallyDrop};
use core::ptr::NonNull;
use crate::convert::traits::{WasmAbi, WasmPrimitive};
use crate::convert::TryFromJsValue;
use crate::convert::{FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, RefFromWasmAbi};
use crate::convert::{OptionFromWasmAbi, OptionIntoWasmAbi, ReturnWasmAbi};
use crate::{Clamped, JsError, JsValue, UnwrapThrowExt};
if_std! {
use std::boxed::Box;
use std::fmt::Debug;
use std::vec::Vec;
}
// Primitive types can always be passed over the ABI.
impl<T: WasmPrimitive> WasmAbi for T {
type Prim1 = Self;
type Prim2 = ();
type Prim3 = ();
type Prim4 = ();
#[inline]
fn split(self) -> (Self, (), (), ()) {
(self, (), (), ())
}
#[inline]
fn join(prim: Self, _: (), _: (), _: ()) -> Self {
prim
}
}
impl<T: WasmAbi<Prim4 = ()>> WasmAbi for Option<T> {
/// Whether this `Option` is a `Some` value.
type Prim1 = u32;
type Prim2 = T::Prim1;
type Prim3 = T::Prim2;
type Prim4 = T::Prim3;
#[inline]
fn split(self) -> (u32, T::Prim1, T::Prim2, T::Prim3) {
match self {
None => (
0,
Default::default(),
Default::default(),
Default::default(),
),
Some(value) => {
let (prim1, prim2, prim3, ()) = value.split();
(1, prim1, prim2, prim3)
}
}
}
#[inline]
fn join(is_some: u32, prim1: T::Prim1, prim2: T::Prim2, prim3: T::Prim3) -> Self {
if is_some == 0 {
None
} else {
Some(T::join(prim1, prim2, prim3, ()))
}
}
}
macro_rules! type_wasm_native {
($($t:tt as $c: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 = Option<$c>;
#[inline]
fn into_abi(self) -> Self::Abi {
self.map(|v| v as $c)
}
}
impl FromWasmAbi for Option<$t> {
type Abi = Option<$c>;
#[inline]
unsafe fn from_abi(js: Self::Abi) -> Self {
js.map(|v: $c| v as $t)
}
}
)*)
}
type_wasm_native!(
i32 as i32
isize as i32
u32 as u32
usize as u32
i64 as i64
u64 as u64
f32 as f32
f64 as f64
);
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);
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 {
// SAFETY: Checked in bindings.
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 Option<*const T> {
type Abi = Option<u32>;
#[inline]
fn into_abi(self) -> Option<u32> {
self.map(|ptr| ptr as u32)
}
}
impl<T> FromWasmAbi for Option<*const T> {
type Abi = Option<u32>;
#[inline]
unsafe fn from_abi(js: Option<u32>) -> Option<*const T> {
js.map(|ptr| ptr 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<T> IntoWasmAbi for Option<*mut T> {
type Abi = Option<u32>;
#[inline]
fn into_abi(self) -> Option<u32> {
self.map(|ptr| ptr as u32)
}
}
impl<T> FromWasmAbi for Option<*mut T> {
type Abi = Option<u32>;
#[inline]
unsafe fn from_abi(js: Option<u32>) -> Option<*mut T> {
js.map(|ptr| ptr as *mut T)
}
}
impl<T> IntoWasmAbi for NonNull<T> {
type Abi = u32;
#[inline]
fn into_abi(self) -> u32 {
self.as_ptr() as u32
}
}
impl<T> OptionIntoWasmAbi for NonNull<T> {
#[inline]
fn none() -> u32 {
0
}
}
impl<T> FromWasmAbi for NonNull<T> {
type Abi = u32;
#[inline]
unsafe fn from_abi(js: Self::Abi) -> Self {
// SAFETY: Checked in bindings.
NonNull::new_unchecked(js as *mut T)
}
}
impl<T> OptionFromWasmAbi for NonNull<T> {
#[inline]
fn is_none(js: &u32) -> bool {
*js == 0
}
}
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 LongRefFromWasmAbi for JsValue {
type Abi = u32;
type Anchor = JsValue;
#[inline]
unsafe fn long_ref_from_abi(js: u32) -> Self::Anchor {
Self::from_abi(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
}
}
impl<T: WasmAbi<Prim3 = (), Prim4 = ()>> WasmAbi for Result<T, u32> {
type Prim1 = T::Prim1;
type Prim2 = T::Prim2;
// The order of primitives here is such that we can pop() the possible error
// first, deal with it and move on. Later primitives are popped off the
// stack first.
/// If this `Result` is an `Err`, the error value.
type Prim3 = u32;
/// Whether this `Result` is an `Err`.
type Prim4 = u32;
#[inline]
fn split(self) -> (T::Prim1, T::Prim2, u32, u32) {
match self {
Ok(value) => {
let (prim1, prim2, (), ()) = value.split();
(prim1, prim2, 0, 0)
}
Err(err) => (Default::default(), Default::default(), err, 1),
}
}
#[inline]
fn join(prim1: T::Prim1, prim2: T::Prim2, err: u32, is_err: u32) -> Self {
if is_err == 0 {
Ok(T::join(prim1, prim2, (), ()))
} else {
Err(err)
}
}
}
impl<T, E> ReturnWasmAbi for Result<T, E>
where
T: IntoWasmAbi,
E: Into<JsValue>,
T::Abi: WasmAbi<Prim3 = (), Prim4 = ()>,
{
type Abi = Result<T::Abi, u32>;
#[inline]
fn return_abi(self) -> Self::Abi {
match self {
Ok(v) => Ok(v.into_abi()),
Err(e) => {
let jsval = e.into();
Err(jsval.into_abi())
}
}
}
}
impl IntoWasmAbi for JsError {
type Abi = <JsValue as IntoWasmAbi>::Abi;
fn into_abi(self) -> Self::Abi {
self.value.into_abi()
}
}
if_std! {
// Note: this can't take `&[T]` because the `Into<JsValue>` impl needs
// ownership of `T`.
pub fn js_value_vector_into_abi<T: Into<JsValue>>(vector: Box<[T]>) -> <Box<[JsValue]> as IntoWasmAbi>::Abi {
let js_vals: Box<[JsValue]> = vector
.into_vec()
.into_iter()
.map(|x| x.into())
.collect();
js_vals.into_abi()
}
pub unsafe fn js_value_vector_from_abi<T: TryFromJsValue>(js: <Box<[JsValue]> as FromWasmAbi>::Abi) -> Box<[T]> where T::Error: Debug {
let js_vals = <Vec<JsValue> as FromWasmAbi>::from_abi(js);
let mut result = Vec::with_capacity(js_vals.len());
for value in js_vals {
// We push elements one-by-one instead of using `collect` in order to improve
// error messages. When using `collect`, this `expect_throw` is buried in a
// giant chain of internal iterator functions, which results in the actual
// function that takes this `Vec` falling off the end of the call stack.
// So instead, make sure to call it directly within this function.
//
// This is only a problem in debug mode. Since this is the browser's error stack
// we're talking about, it can only see functions that actually make it to the
// final wasm binary (i.e., not inlined functions). All of those internal
// iterator functions get inlined in release mode, and so they don't show up.
result.push(T::try_from_js_value(value).expect_throw("array contains a value of the wrong type"));
}
result.into_boxed_slice()
}
}

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,456 @@
#[cfg(feature = "std")]
use std::prelude::v1::*;
use core::ops::{Deref, DerefMut};
use core::str;
use crate::__wbindgen_copy_to_typed_array;
use crate::cast::JsObject;
use crate::convert::OptionIntoWasmAbi;
use crate::convert::{
FromWasmAbi, IntoWasmAbi, LongRefFromWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi,
};
use crate::convert::{VectorFromWasmAbi, VectorIntoWasmAbi};
use crate::describe::*;
use cfg_if::cfg_if;
if_std! {
use core::mem;
use crate::convert::OptionFromWasmAbi;
use crate::convert::{js_value_vector_from_abi, js_value_vector_into_abi};
}
// note: `WasmAbi` types do not need to be FFI-safe themselves, it's just more
// convenient to directly write `WasmSlice` in some of the manually-written FFI
// functions in `lib.rs` rather than `WasmRet<WasmSlice>`.
#[repr(C)]
pub struct WasmSlice {
pub ptr: u32,
pub len: u32,
}
impl WasmAbi for WasmSlice {
/// `self.ptr`
type Prim1 = u32;
/// `self.len`
type Prim2 = u32;
type Prim3 = ();
type Prim4 = ();
#[inline]
fn split(self) -> (u32, u32, (), ()) {
(self.ptr, self.len, (), ())
}
#[inline]
fn join(ptr: u32, len: u32, _: (), _: ()) -> Self {
Self { ptr, len }
}
}
#[inline]
fn null_slice() -> WasmSlice {
WasmSlice { ptr: 0, len: 0 }
}
if_std! {
pub struct WasmMutSlice {
pub slice: WasmSlice,
pub idx: u32,
}
impl WasmAbi for WasmMutSlice {
/// `self.slice.ptr`
type Prim1 = u32;
/// `self.slice.len`
type Prim2 = u32;
/// `self.idx`
type Prim3 = u32;
type Prim4 = ();
#[inline]
fn split(self) -> (u32, u32, u32, ()) {
(self.slice.ptr, self.slice.len, self.idx, ())
}
#[inline]
fn join(ptr: u32, len: u32, idx: u32, _: ()) -> Self {
Self {
slice: WasmSlice { ptr, len },
idx,
}
}
}
/// The representation of a mutable slice passed from JS to Rust.
pub struct MutSlice<T> {
/// A copy of the data in the JS typed array.
contents: Box<[T]>,
/// A reference to the original JS typed array.
js: JsValue,
}
impl<T> Drop for MutSlice<T> {
fn drop(&mut self) {
unsafe {
__wbindgen_copy_to_typed_array(
self.contents.as_ptr() as *const u8,
self.contents.len() * mem::size_of::<T>(),
self.js.idx
);
}
}
}
impl<T> Deref for MutSlice<T> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.contents
}
}
impl<T> DerefMut for MutSlice<T> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.contents
}
}
}
macro_rules! vectors {
($($t:ident)*) => ($(
if_std! {
impl WasmDescribeVector for $t {
fn describe_vector() {
inform(VECTOR);
$t::describe();
}
}
impl VectorIntoWasmAbi for $t {
type Abi = WasmSlice;
#[inline]
fn vector_into_abi(vector: Box<[$t]>) -> WasmSlice {
let ptr = vector.as_ptr();
let len = vector.len();
mem::forget(vector);
WasmSlice {
ptr: ptr.into_abi(),
len: len as u32,
}
}
}
impl VectorFromWasmAbi for $t {
type Abi = WasmSlice;
#[inline]
unsafe fn vector_from_abi(js: WasmSlice) -> Box<[$t]> {
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<'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 = WasmMutSlice;
type Anchor = MutSlice<$t>;
#[inline]
unsafe fn ref_mut_from_abi(js: WasmMutSlice) -> MutSlice<$t> {
let contents = <Box<[$t]>>::from_abi(js.slice);
let js = JsValue::from_abi(js.idx);
MutSlice { contents, js }
}
}
impl LongRefFromWasmAbi for [$t] {
type Abi = WasmSlice;
type Anchor = Box<[$t]>;
#[inline]
unsafe fn long_ref_from_abi(js: WasmSlice) -> Box<[$t]> {
Self::ref_from_abi(js)
}
}
)*)
}
vectors! {
u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64
}
if_std! {
impl WasmDescribeVector for String {
fn describe_vector() {
inform(VECTOR);
inform(NAMED_EXTERNREF);
// Trying to use an actual loop for this breaks the wasm interpreter.
inform(6);
inform('s' as u32);
inform('t' as u32);
inform('r' as u32);
inform('i' as u32);
inform('n' as u32);
inform('g' as u32);
}
}
impl VectorIntoWasmAbi for String {
type Abi = <Box<[JsValue]> as IntoWasmAbi>::Abi;
fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi {
js_value_vector_into_abi(vector)
}
}
impl VectorFromWasmAbi for String {
type Abi = <Box<[JsValue]> as FromWasmAbi>::Abi;
unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]> {
js_value_vector_from_abi(js)
}
}
}
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))
}
}
impl LongRefFromWasmAbi for str {
type Abi = <[u8] as RefFromWasmAbi>::Abi;
type Anchor = Box<str>;
#[inline]
unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
Self::ref_from_abi(js)
}
}
if_std! {
use crate::JsValue;
impl<T: VectorIntoWasmAbi> IntoWasmAbi for Box<[T]> {
type Abi = <T as VectorIntoWasmAbi>::Abi;
fn into_abi(self) -> Self::Abi {
T::vector_into_abi(self)
}
}
impl<T> OptionIntoWasmAbi for Box<[T]> where Self: IntoWasmAbi<Abi = WasmSlice> {
fn none() -> WasmSlice {
null_slice()
}
}
impl<T: VectorFromWasmAbi> FromWasmAbi for Box<[T]> {
type Abi = <T as VectorFromWasmAbi>::Abi;
unsafe fn from_abi(js: Self::Abi) -> Self {
T::vector_from_abi(js)
}
}
impl<T> OptionFromWasmAbi for Box<[T]> where Self: FromWasmAbi<Abi = WasmSlice> {
fn is_none(slice: &WasmSlice) -> bool {
slice.ptr == 0
}
}
impl VectorIntoWasmAbi for JsValue {
type Abi = WasmSlice;
#[inline]
fn vector_into_abi(vector: Box<[Self]>) -> WasmSlice {
let ptr = vector.as_ptr();
let len = vector.len();
mem::forget(vector);
WasmSlice {
ptr: ptr.into_abi(),
len: len as u32,
}
}
}
impl VectorFromWasmAbi for JsValue {
type Abi = WasmSlice;
#[inline]
unsafe fn vector_from_abi(js: WasmSlice) -> Box<[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<T> VectorIntoWasmAbi for T where T: JsObject {
type Abi = WasmSlice;
#[inline]
fn vector_into_abi(vector: Box<[T]>) -> WasmSlice {
let ptr = vector.as_ptr();
let len = vector.len();
mem::forget(vector);
WasmSlice {
ptr: ptr.into_abi(),
len: len as u32,
}
}
}
impl<T> VectorFromWasmAbi for T where T: JsObject {
type Abi = WasmSlice;
#[inline]
unsafe fn vector_from_abi(js: WasmSlice) -> Box<[T]> {
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();
vec.into_boxed_slice()
}
}
}

View file

@ -0,0 +1,267 @@
use core::borrow::Borrow;
use core::ops::{Deref, DerefMut};
use crate::describe::*;
use crate::JsValue;
/// 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;
}
/// A version of the `RefFromWasmAbi` trait with the additional requirement
/// that the reference must remain valid as long as the anchor isn't dropped.
///
/// This isn't the case for `JsValue`'s `RefFromWasmAbi` implementation. To
/// avoid having to allocate a spot for the `JsValue` on the `JsValue` heap,
/// the `JsValue` is instead pushed onto the `JsValue` stack, and popped off
/// again after the function that the reference was passed to returns. So,
/// `JsValue` has a different `LongRefFromWasmAbi` implementation that behaves
/// the same as `FromWasmAbi`, putting the value on the heap.
///
/// This is needed for async functions, where the reference needs to be valid
/// for the whole length of the `Future`, rather than the initial synchronous
/// call.
///
/// 'long ref' is short for 'long-lived reference'.
pub trait LongRefFromWasmAbi: WasmDescribe {
/// Same as `RefFromWasmAbi::Abi`
type Abi: WasmAbi;
/// Same as `RefFromWasmAbi::Anchor`
type Anchor: Borrow<Self>;
/// Same as `RefFromWasmAbi::ref_from_abi`
unsafe fn long_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;
}
/// A trait for any type which maps to a Wasm primitive type when used in FFI
/// (`i32`, `i64`, `f32`, or `f64`).
///
/// This is with the exception of `()` (and other zero-sized types), which are
/// also allowed because they're ignored: no arguments actually get added.
///
/// # Safety
///
/// This is an unsafe trait to implement as there's no guarantee the type
/// actually maps to a primitive type.
pub unsafe trait WasmPrimitive: Default {}
unsafe impl WasmPrimitive for u32 {}
unsafe impl WasmPrimitive for i32 {}
unsafe impl WasmPrimitive for u64 {}
unsafe impl WasmPrimitive for i64 {}
unsafe impl WasmPrimitive for f32 {}
unsafe impl WasmPrimitive for f64 {}
unsafe impl WasmPrimitive for () {}
/// A trait which represents types that can be passed across the Wasm ABI
/// boundary, by being split into multiple Wasm primitive types.
///
/// Up to 4 primitives are supported; if you don't want to use all of them, you
/// can set the rest to `()`, which will cause them to be ignored.
///
/// You need to be careful how many primitives you use, however:
/// `Result<T, JsValue>` uses up 2 primitives to store the error, and so it
/// doesn't work if `T` uses more than 2 primitives.
///
/// So, if you're adding support for a type that needs 3 or more primitives and
/// is able to be returned, you have to add another primitive here.
///
/// There's already one type that uses 3 primitives: `&mut [T]`. However, it
/// can't be returned anyway, so it doesn't matter that
/// `Result<&mut [T], JsValue>` wouldn't work.
pub trait WasmAbi {
type Prim1: WasmPrimitive;
type Prim2: WasmPrimitive;
type Prim3: WasmPrimitive;
type Prim4: WasmPrimitive;
/// Splits this type up into primitives to be sent over the ABI.
fn split(self) -> (Self::Prim1, Self::Prim2, Self::Prim3, Self::Prim4);
/// Reconstructs this type from primitives received over the ABI.
fn join(prim1: Self::Prim1, prim2: Self::Prim2, prim3: Self::Prim3, prim4: Self::Prim4)
-> Self;
}
/// A trait representing how to interpret 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()
}
}
if_std! {
use core::marker::Sized;
use std::boxed::Box;
/// Trait for element types to implement IntoWasmAbi for vectors of
/// themselves.
pub trait VectorIntoWasmAbi: WasmDescribeVector + Sized {
type Abi: WasmAbi;
fn vector_into_abi(vector: Box<[Self]>) -> Self::Abi;
}
/// Trait for element types to implement FromWasmAbi for vectors of
/// themselves.
pub trait VectorFromWasmAbi: WasmDescribeVector + Sized {
type Abi: WasmAbi;
unsafe fn vector_from_abi(js: Self::Abi) -> Box<[Self]>;
}
}
/// A repr(C) struct containing all of the primitives of a `WasmAbi` type, in
/// order.
///
/// This is used as the return type of imported/exported functions. `WasmAbi`
/// types aren't guaranteed to be FFI-safe, so we can't return them directly:
/// instead we return this.
///
/// If all but one of the primitives is `()`, this corresponds to returning the
/// remaining primitive directly, otherwise a return pointer is used.
#[repr(C)]
pub struct WasmRet<T: WasmAbi> {
prim1: T::Prim1,
prim2: T::Prim2,
prim3: T::Prim3,
prim4: T::Prim4,
}
impl<T: WasmAbi> From<T> for WasmRet<T> {
fn from(value: T) -> Self {
let (prim1, prim2, prim3, prim4) = value.split();
Self {
prim1,
prim2,
prim3,
prim4,
}
}
}
// Ideally this'd just be an `Into<T>` implementation, but unfortunately that
// doesn't work because of the orphan rule.
impl<T: WasmAbi> WasmRet<T> {
/// Joins the components of this `WasmRet` back into the type they represent.
pub fn join(self) -> T {
T::join(self.prim1, self.prim2, self.prim3, self.prim4)
}
}
/// [`TryFromJsValue`] is a trait for converting a JavaScript value ([`JsValue`])
/// into a Rust type. It is used by the [`wasm_bindgen`](wasm_bindgen_macro::wasm_bindgen)
/// proc-macro to allow conversion to user types.
///
/// Types implementing this trait must specify their conversion logic from
/// [`JsValue`] to the Rust type, handling any potential errors that may occur
/// during the conversion process.
pub trait TryFromJsValue: Sized {
/// The type returned in the event of a conversion error.
type Error;
/// Performs the conversion.
fn try_from_js_value(value: JsValue) -> Result<Self, Self::Error>;
}

View file

@ -0,0 +1,221 @@
//! This is an internal module, no stability guarantees are provided. Use at
//! your own risk.
#![doc(hidden)]
use core::ptr::NonNull;
use crate::{Clamped, JsError, JsObject, JsValue};
use cfg_if::cfg_if;
macro_rules! tys {
($($a:ident)*) => (tys! { @ ($($a)*) 0 });
(@ () $v:expr) => {};
(@ ($a:ident $($b:ident)*) $v:expr) => {
pub const $a: u32 = $v;
tys!(@ ($($b)*) $v+1);
}
}
// NB: this list must be kept in sync with `crates/cli-support/src/descriptor.rs`
tys! {
I8
U8
I16
U16
I32
U32
I64
U64
F32
F64
BOOLEAN
FUNCTION
CLOSURE
CACHED_STRING
STRING
REF
REFMUT
LONGREF
SLICE
VECTOR
EXTERNREF
NAMED_EXTERNREF
ENUM
RUST_STRUCT
CHAR
OPTIONAL
RESULT
UNIT
CLAMPED
NONNULL
}
#[inline(always)] // see the wasm-interpreter crate
pub fn inform(a: u32) {
unsafe { super::__wbindgen_describe(a) }
}
pub trait WasmDescribe {
fn describe();
}
/// Trait for element types to implement WasmDescribe for vectors of
/// themselves.
pub trait WasmDescribeVector {
fn describe_vector();
}
macro_rules! simple {
($($t:ident => $d:ident)*) => ($(
impl WasmDescribe for $t {
fn describe() { inform($d) }
}
)*)
}
simple! {
i8 => I8
u8 => U8
i16 => I16
u16 => U16
i32 => I32
u32 => U32
i64 => I64
u64 => U64
isize => I32
usize => U32
f32 => F32
f64 => F64
bool => BOOLEAN
char => CHAR
JsValue => EXTERNREF
}
cfg_if! {
if #[cfg(feature = "enable-interning")] {
simple! {
str => CACHED_STRING
}
} else {
simple! {
str => STRING
}
}
}
impl<T> WasmDescribe for *const T {
fn describe() {
inform(U32)
}
}
impl<T> WasmDescribe for *mut T {
fn describe() {
inform(U32)
}
}
impl<T> WasmDescribe for NonNull<T> {
fn describe() {
inform(NONNULL)
}
}
impl<T: WasmDescribe> WasmDescribe for [T] {
fn describe() {
inform(SLICE);
T::describe();
}
}
impl<'a, T: WasmDescribe + ?Sized> WasmDescribe for &'a T {
fn describe() {
inform(REF);
T::describe();
}
}
impl<'a, T: WasmDescribe + ?Sized> WasmDescribe for &'a mut T {
fn describe() {
inform(REFMUT);
T::describe();
}
}
if_std! {
use std::prelude::v1::*;
cfg_if! {
if #[cfg(feature = "enable-interning")] {
simple! {
String => CACHED_STRING
}
} else {
simple! {
String => STRING
}
}
}
impl WasmDescribeVector for JsValue {
fn describe_vector() {
inform(VECTOR);
JsValue::describe();
}
}
impl<T: JsObject> WasmDescribeVector for T {
fn describe_vector() {
inform(VECTOR);
T::describe();
}
}
impl<T: WasmDescribeVector> WasmDescribe for Box<[T]> {
fn describe() {
T::describe_vector();
}
}
impl<T> WasmDescribe for Vec<T> where Box<[T]>: WasmDescribe {
fn describe() {
<Box<[T]>>::describe();
}
}
}
impl<T: WasmDescribe> WasmDescribe for Option<T> {
fn describe() {
inform(OPTIONAL);
T::describe();
}
}
impl WasmDescribe for () {
fn describe() {
inform(UNIT)
}
}
impl<T: WasmDescribe, E: Into<JsValue>> WasmDescribe for Result<T, E> {
fn describe() {
inform(RESULT);
T::describe();
}
}
impl<T: WasmDescribe> WasmDescribe for Clamped<T> {
fn describe() {
inform(CLAMPED);
T::describe();
}
}
impl WasmDescribe for JsError {
fn describe() {
JsValue::describe();
}
}

View file

@ -0,0 +1,166 @@
use crate::JsValue;
use std::cell::Cell;
use std::slice;
use std::vec::Vec;
use std::cmp::max;
externs! {
#[link(wasm_import_module = "__wbindgen_externref_xform__")]
extern "C" {
fn __wbindgen_externref_table_grow(delta: usize) -> i32;
fn __wbindgen_externref_table_set_null(idx: usize) -> ();
}
}
pub struct Slab {
data: Vec<usize>,
head: usize,
base: usize,
}
impl Slab {
fn new() -> Slab {
Slab {
data: Vec::new(),
head: 0,
base: 0,
}
}
fn alloc(&mut self) -> usize {
let ret = self.head;
if ret == self.data.len() {
let curr_len = self.data.len();
if curr_len == self.data.capacity() {
let extra = max(128, curr_len);
let r = unsafe { __wbindgen_externref_table_grow(extra) };
if r == -1 {
internal_error("table grow failure")
}
if self.base == 0 {
self.base = r as usize;
} else if self.base + self.data.len() != r as usize {
internal_error("someone else allocated table entries?")
}
if self.data.try_reserve_exact(extra).is_err() {
internal_error("allocation failure");
}
}
// custom condition to ensure `push` below doesn't call `reserve` in
// optimized builds which pulls in lots of panic infrastructure
if self.data.len() >= self.data.capacity() {
internal_error("push should be infallible now")
}
self.data.push(ret + 1);
}
// usage of `get_mut` thwarts panicking infrastructure in optimized
// builds
match self.data.get_mut(ret) {
Some(slot) => self.head = *slot,
None => internal_error("ret out of bounds"),
}
ret + self.base
}
fn dealloc(&mut self, slot: usize) {
if slot < self.base {
internal_error("free reserved slot");
}
let slot = slot - self.base;
// usage of `get_mut` thwarts panicking infrastructure in optimized
// builds
match self.data.get_mut(slot) {
Some(ptr) => {
*ptr = self.head;
self.head = slot;
}
None => internal_error("slot out of bounds"),
}
}
fn live_count(&self) -> u32 {
let mut free_count = 0;
let mut next = self.head;
while next < self.data.len() {
debug_assert!((free_count as usize) < self.data.len());
free_count += 1;
match self.data.get(next) {
Some(n) => next = *n,
None => internal_error("slot out of bounds"),
};
}
self.data.len() as u32 - free_count
}
}
fn internal_error(msg: &str) -> ! {
if cfg!(debug_assertions) {
super::throw_str(msg)
} else {
std::process::abort()
}
}
// Management of `externref` is always thread local since an `externref` value
// can't cross threads in wasm. Indices as a result are always thread-local.
std::thread_local!(pub static HEAP_SLAB: Cell<Slab> = Cell::new(Slab::new()));
#[no_mangle]
pub extern "C" fn __externref_table_alloc() -> usize {
HEAP_SLAB
.try_with(|slot| {
let mut slab = slot.replace(Slab::new());
let ret = slab.alloc();
slot.replace(slab);
ret
})
.unwrap_or_else(|_| internal_error("tls access failure"))
}
#[no_mangle]
pub extern "C" fn __externref_table_dealloc(idx: usize) {
if idx < super::JSIDX_RESERVED as usize {
return;
}
// clear this value from the table so while the table slot is un-allocated
// we don't keep around a strong reference to a potentially large object
unsafe {
__wbindgen_externref_table_set_null(idx);
}
HEAP_SLAB
.try_with(|slot| {
let mut slab = slot.replace(Slab::new());
slab.dealloc(idx);
slot.replace(slab);
})
.unwrap_or_else(|_| internal_error("tls access failure"))
}
#[no_mangle]
pub unsafe extern "C" fn __externref_drop_slice(ptr: *mut JsValue, len: usize) {
for slot in slice::from_raw_parts_mut(ptr, len) {
__externref_table_dealloc(slot.idx as usize);
}
}
// Implementation of `__wbindgen_externref_heap_live_count` for when we are using
// `externref` instead of the JS `heap`.
#[no_mangle]
pub unsafe extern "C" fn __externref_heap_live_count() -> u32 {
HEAP_SLAB
.try_with(|slot| {
let slab = slot.replace(Slab::new());
let count = slab.live_count();
slot.replace(slab);
count
})
.unwrap_or_else(|_| internal_error("tls access failure"))
}
// see comment in module above this in `link_mem_intrinsics`
#[inline(never)]
pub fn link_intrinsics() {}

1914
third-party/vendor/wasm-bindgen/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load diff