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,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;

160
zeroidc/vendor/wasm-bindgen/src/cast.rs vendored Normal file
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 differnet 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,871 @@
//! 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.
use std::fmt;
#[cfg(feature = "nightly")]
use std::marker::Unsize;
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). The arguments/return value of the
/// trait must be numbers like `u32` for now, although this restriction may be
/// lifted in the future!
///
/// # 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::wrap` to wrap up a Rust closure and create
/// // a JS closure.
/// let cb = Closure::wrap(Box::new(|| {
/// log("interval elapsed!");
/// }) as Box<dyn FnMut()>);
///
/// // 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::wrap(Box::new(|| {
/// web_sys::console::log_1(&"interval elapsed!".into());
/// }) as Box<dyn FnMut()>);
///
/// 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,
{
/// A more ergonomic version of `Closure::wrap` that does the boxing and
/// cast to trait object for you.
///
/// *This method requires the `nightly` feature of the `wasm-bindgen` crate
/// to be enabled, meaning this is a nightly-only API. Users on stable
/// should use `Closure::wrap`.*
#[cfg(feature = "nightly")]
pub fn new<F>(t: F) -> Closure<T>
where
F: Unsize<T> + 'static,
{
Closure::wrap(Box::new(t) as Box<T>)
}
/// Creates a new instance of `Closure` from the provided boxed Rust
/// function.
///
/// Note that the closure provided here, `Box<T>`, 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 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.
///
/// By default this function will leak memory. 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.
///
/// If the browser, however, supports weak references, then this function
/// will not leak memory. Instead the Rust memory will be reclaimed when the
/// JS closure is GC'd. Weak references are not enabled by default since
/// they're still a proposal for the JS standard. They can be enabled with
/// `WASM_BINDGEN_WEAKREF=1` when running `wasm-bindgen`, however.
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 js_sys;
/// 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();
}
// 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)*)
)*) => ($(
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,
$($var: <$var as FromWasmAbi>::Abi),*
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// 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);
)*
(*f)($($var),*)
};
ret.return_abi()
}
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,
$($var: <$var as FromWasmAbi>::Abi),*
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// 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);
)*
(*f)($($var),*)
};
ret.return_abi()
}
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
}
}
)*)
}
doit! {
()
(A)
(A B)
(A B C)
(A B C D)
(A B C D E)
(A B C D E F)
(A B C D E F G)
(A B C D E F G H)
}
// 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,
arg: <A as RefFromWasmAbi>::Abi,
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// 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(arg);
(*f)(&*arg)
};
ret.return_abi()
}
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,
arg: <A as RefFromWasmAbi>::Abi,
) -> <R as ReturnWasmAbi>::Abi {
if a == 0 {
throw_str("closure invoked recursively or destroyed already");
}
// 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(arg);
(*f)(&*arg)
};
ret.return_abi()
}
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,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()
}
}

View File

@@ -0,0 +1,192 @@
//! This is an internal module, no stability guarantees are provided. Use at
//! your own risk.
#![doc(hidden)]
use crate::{Clamped, JsError, 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
SLICE
VECTOR
EXTERNREF
NAMED_EXTERNREF
ENUM
RUST_STRUCT
CHAR
OPTIONAL
RESULT
UNIT
CLAMPED
}
#[inline(always)] // see `interpret.rs` in the the cli-support crate
pub fn inform(a: u32) {
unsafe { super::__wbindgen_describe(a) }
}
pub trait WasmDescribe {
fn describe();
}
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(I32)
}
}
impl<T> WasmDescribe for *mut T {
fn describe() {
inform(I32)
}
}
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<T: WasmDescribe> WasmDescribe for Box<[T]> {
fn describe() {
inform(VECTOR);
T::describe();
}
}
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,184 @@
use crate::JsValue;
use std::alloc::{self, Layout};
use std::cell::Cell;
use std::mem;
use std::ptr;
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 entires?")
}
// poor man's `try_reserve_exact` until that's stable
unsafe {
let new_cap = self.data.capacity() + extra;
let size = mem::size_of::<usize>() * new_cap;
let align = mem::align_of::<usize>();
let layout = match Layout::from_size_align(size, align) {
Ok(l) => l,
Err(_) => internal_error("size/align layout failure"),
};
let ptr = alloc::alloc(layout) as *mut usize;
if ptr.is_null() {
internal_error("allocation failure");
}
ptr::copy_nonoverlapping(self.data.as_ptr(), ptr, self.data.len());
let new_vec = Vec::from_raw_parts(ptr, self.data.len(), new_cap);
let mut old = mem::replace(&mut self.data, new_vec);
old.set_len(0);
}
}
// 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() {}

1684
zeroidc/vendor/wasm-bindgen/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff