diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/wasm-bindgen-futures | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/wasm-bindgen-futures')
-rw-r--r-- | vendor/wasm-bindgen-futures/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/Cargo.toml | 56 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/LICENSE-MIT | 25 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/README.md | 16 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/src/lib.rs | 229 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/src/queue.rs | 91 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/src/stream.rs | 81 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/src/task/multithread.rs | 201 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/src/task/singlethread.rs | 113 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/src/task/wait_async_polyfill.rs | 91 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/src/task/worker.js | 6 | ||||
-rw-r--r-- | vendor/wasm-bindgen-futures/tests/tests.rs | 157 |
13 files changed, 1268 insertions, 0 deletions
diff --git a/vendor/wasm-bindgen-futures/.cargo-checksum.json b/vendor/wasm-bindgen-futures/.cargo-checksum.json new file mode 100644 index 000000000..ea84514d2 --- /dev/null +++ b/vendor/wasm-bindgen-futures/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"ee7ebbb384e7bad80c2e62af9c6fd2b9eab4af7e95d0e93cedb1410c7952395a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"85d37419c6d458e5076b37ed6f06471491b1b16b29dc1e37972117ed2818652d","src/lib.rs":"6c3a7fb9a25793434861f66bab405b5f446407a2b46055bb45bf87b7593a262e","src/queue.rs":"80fe49bc6a7235ce3388d6871da5a24e0fd3c32da0e19d96eeb5f60922167348","src/stream.rs":"5e83d72eb8c339ef0208c367da0004160c9f567ac744bc66e8e49de9589a1825","src/task/multithread.rs":"d25b63e2f0679bce9e79aa85b4c94afb6ba656118f7e813b84eb96f384448bd0","src/task/singlethread.rs":"623a221b4db670588ae115627a9b7f81e21f4827259ac719abb7ef5b1cefa9a5","src/task/wait_async_polyfill.rs":"6245e6c6040b36025aed6b2d880866ede6e7bd97ec68676d0a754c09916f440c","src/task/worker.js":"1f1b207b7f45925d8c50f51538d3c23caf14073080bfe6e6bc64a3689b93db78","tests/tests.rs":"7ad80fd9f0ba3b1d05015d05fbdc16891b81ac1d975e0498c04b503fbf62c3e8"},"package":"f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"}
\ No newline at end of file diff --git a/vendor/wasm-bindgen-futures/Cargo.toml b/vendor/wasm-bindgen-futures/Cargo.toml new file mode 100644 index 000000000..885246b36 --- /dev/null +++ b/vendor/wasm-bindgen-futures/Cargo.toml @@ -0,0 +1,56 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "wasm-bindgen-futures" +version = "0.4.34" +authors = ["The wasm-bindgen Developers"] +description = "Bridging the gap between Rust Futures and JavaScript Promises" +homepage = "https://rustwasm.github.io/wasm-bindgen/" +documentation = "https://docs.rs/wasm-bindgen-futures" +readme = "./README.md" +license = "MIT/Apache-2.0" +repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures" + +[dependencies.cfg-if] +version = "1.0.0" + +[dependencies.futures-core] +version = "0.3.8" +optional = true +default-features = false + +[dependencies.js-sys] +version = "0.3.61" + +[dependencies.wasm-bindgen] +version = "0.2.84" + +[features] +futures-core-03-stream = ["futures-core"] + +[target."cfg(target_arch = \"wasm32\")".dev-dependencies.futures-channel-preview] +version = "0.3.0-alpha.18" + +[target."cfg(target_arch = \"wasm32\")".dev-dependencies.futures-lite] +version = "1.11.3" +default-features = false + +[target."cfg(target_arch = \"wasm32\")".dev-dependencies.wasm-bindgen-test] +version = "0.3.34" + +[target."cfg(target_feature = \"atomics\")".dependencies.web-sys] +version = "0.3.24" +features = [ + "MessageEvent", + "Worker", +] diff --git a/vendor/wasm-bindgen-futures/LICENSE-APACHE b/vendor/wasm-bindgen-futures/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/vendor/wasm-bindgen-futures/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/wasm-bindgen-futures/LICENSE-MIT b/vendor/wasm-bindgen-futures/LICENSE-MIT new file mode 100644 index 000000000..39e0ed660 --- /dev/null +++ b/vendor/wasm-bindgen-futures/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/wasm-bindgen-futures/README.md b/vendor/wasm-bindgen-futures/README.md new file mode 100644 index 000000000..eb0a067f3 --- /dev/null +++ b/vendor/wasm-bindgen-futures/README.md @@ -0,0 +1,16 @@ +# `wasm-bindgen-futures` + +[API Documention][docs] + +This crate bridges the gap between a Rust `Future` and a JavaScript +`Promise`. It provides two conversions: + +1. From a JavaScript `Promise` into a Rust `Future`. +2. From a Rust `Future` into a JavaScript `Promise`. + +Additionally under the feature flag `futures-core-03-stream` there is experimental +support for `AsyncIterator` to `Stream` conversion. + +See the [API documentation][docs] for more info. + +[docs]: https://rustwasm.github.io/wasm-bindgen/api/wasm_bindgen_futures/ diff --git a/vendor/wasm-bindgen-futures/src/lib.rs b/vendor/wasm-bindgen-futures/src/lib.rs new file mode 100644 index 000000000..c2a59d762 --- /dev/null +++ b/vendor/wasm-bindgen-futures/src/lib.rs @@ -0,0 +1,229 @@ +//! Converting between JavaScript `Promise`s to Rust `Future`s. +//! +//! This crate provides a bridge for working with JavaScript `Promise` types as +//! a Rust `Future`, and similarly contains utilities to turn a rust `Future` +//! into a JavaScript `Promise`. This can be useful when working with +//! asynchronous or otherwise blocking work in Rust (wasm), and provides the +//! ability to interoperate with JavaScript events and JavaScript I/O +//! primitives. +//! +//! There are three main interfaces in this crate currently: +//! +//! 1. [**`JsFuture`**](./struct.JsFuture.html) +//! +//! A type that is constructed with a `Promise` and can then be used as a +//! `Future<Output = Result<JsValue, JsValue>>`. This Rust future will resolve +//! or reject with the value coming out of the `Promise`. +//! +//! 2. [**`future_to_promise`**](./fn.future_to_promise.html) +//! +//! Converts a Rust `Future<Output = Result<JsValue, JsValue>>` into a +//! JavaScript `Promise`. The future's result will translate to either a +//! resolved or rejected `Promise` in JavaScript. +//! +//! 3. [**`spawn_local`**](./fn.spawn_local.html) +//! +//! Spawns a `Future<Output = ()>` on the current thread. This is the +//! best way to run a `Future` in Rust without sending it to JavaScript. +//! +//! These three items should provide enough of a bridge to interoperate the two +//! systems and make sure that Rust/JavaScript can work together with +//! asynchronous and I/O work. + +#![cfg_attr(target_feature = "atomics", feature(stdsimd))] +#![deny(missing_docs)] + +use js_sys::Promise; +use std::cell::RefCell; +use std::fmt; +use std::future::Future; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll, Waker}; +use wasm_bindgen::prelude::*; + +mod queue; +#[cfg(feature = "futures-core-03-stream")] +pub mod stream; + +mod task { + use cfg_if::cfg_if; + + cfg_if! { + if #[cfg(target_feature = "atomics")] { + mod wait_async_polyfill; + mod multithread; + pub(crate) use multithread::*; + + } else { + mod singlethread; + pub(crate) use singlethread::*; + } + } +} + +/// Runs a Rust `Future` on the current thread. +/// +/// The `future` must be `'static` because it will be scheduled +/// to run in the background and cannot contain any stack references. +/// +/// The `future` will always be run on the next microtask tick even if it +/// immediately returns `Poll::Ready`. +/// +/// # Panics +/// +/// This function has the same panic behavior as `future_to_promise`. +#[inline] +pub fn spawn_local<F>(future: F) +where + F: Future<Output = ()> + 'static, +{ + task::Task::spawn(Box::pin(future)); +} + +struct Inner { + result: Option<Result<JsValue, JsValue>>, + task: Option<Waker>, + callbacks: Option<(Closure<dyn FnMut(JsValue)>, Closure<dyn FnMut(JsValue)>)>, +} + +/// A Rust `Future` backed by a JavaScript `Promise`. +/// +/// This type is constructed with a JavaScript `Promise` object and translates +/// it to a Rust `Future`. This type implements the `Future` trait from the +/// `futures` crate and will either succeed or fail depending on what happens +/// with the JavaScript `Promise`. +/// +/// Currently this type is constructed with `JsFuture::from`. +pub struct JsFuture { + inner: Rc<RefCell<Inner>>, +} + +impl fmt::Debug for JsFuture { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "JsFuture {{ ... }}") + } +} + +impl From<Promise> for JsFuture { + fn from(js: Promise) -> JsFuture { + // Use the `then` method to schedule two callbacks, one for the + // resolved value and one for the rejected value. We're currently + // assuming that JS engines will unconditionally invoke precisely one of + // these callbacks, no matter what. + // + // Ideally we'd have a way to cancel the callbacks getting invoked and + // free up state ourselves when this `JsFuture` is dropped. We don't + // have that, though, and one of the callbacks is likely always going to + // be invoked. + // + // As a result we need to make sure that no matter when the callbacks + // are invoked they are valid to be called at any time, which means they + // have to be self-contained. Through the `Closure::once` and some + // `Rc`-trickery we can arrange for both instances of `Closure`, and the + // `Rc`, to all be destroyed once the first one is called. + let state = Rc::new(RefCell::new(Inner { + result: None, + task: None, + callbacks: None, + })); + + fn finish(state: &RefCell<Inner>, val: Result<JsValue, JsValue>) { + let task = { + let mut state = state.borrow_mut(); + debug_assert!(state.callbacks.is_some()); + debug_assert!(state.result.is_none()); + + // First up drop our closures as they'll never be invoked again and + // this is our chance to clean up their state. + drop(state.callbacks.take()); + + // Next, store the value into the internal state. + state.result = Some(val); + state.task.take() + }; + + // And then finally if any task was waiting on the value wake it up and + // let them know it's there. + if let Some(task) = task { + task.wake() + } + } + + let resolve = { + let state = state.clone(); + Closure::once(move |val| finish(&state, Ok(val))) + }; + + let reject = { + let state = state.clone(); + Closure::once(move |val| finish(&state, Err(val))) + }; + + let _ = js.then2(&resolve, &reject); + + state.borrow_mut().callbacks = Some((resolve, reject)); + + JsFuture { inner: state } + } +} + +impl Future for JsFuture { + type Output = Result<JsValue, JsValue>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { + let mut inner = self.inner.borrow_mut(); + + // If our value has come in then we return it... + if let Some(val) = inner.result.take() { + return Poll::Ready(val); + } + + // ... otherwise we arrange ourselves to get woken up once the value + // does come in + inner.task = Some(cx.waker().clone()); + Poll::Pending + } +} + +/// Converts a Rust `Future` into a JavaScript `Promise`. +/// +/// This function will take any future in Rust and schedule it to be executed, +/// returning a JavaScript `Promise` which can then be passed to JavaScript. +/// +/// The `future` must be `'static` because it will be scheduled to run in the +/// background and cannot contain any stack references. +/// +/// The returned `Promise` will be resolved or rejected when the future completes, +/// depending on whether it finishes with `Ok` or `Err`. +/// +/// # Panics +/// +/// Note that in wasm panics are currently translated to aborts, but "abort" in +/// this case means that a JavaScript exception is thrown. The wasm module is +/// still usable (likely erroneously) after Rust panics. +/// +/// If the `future` provided panics then the returned `Promise` **will not +/// resolve**. Instead it will be a leaked promise. This is an unfortunate +/// limitation of wasm currently that's hoped to be fixed one day! +pub fn future_to_promise<F>(future: F) -> Promise +where + F: Future<Output = Result<JsValue, JsValue>> + 'static, +{ + let mut future = Some(future); + + Promise::new(&mut |resolve, reject| { + let future = future.take().unwrap_throw(); + + spawn_local(async move { + match future.await { + Ok(val) => { + resolve.call1(&JsValue::undefined(), &val).unwrap_throw(); + } + Err(val) => { + reject.call1(&JsValue::undefined(), &val).unwrap_throw(); + } + } + }); + }) +} diff --git a/vendor/wasm-bindgen-futures/src/queue.rs b/vendor/wasm-bindgen-futures/src/queue.rs new file mode 100644 index 000000000..9a78571cd --- /dev/null +++ b/vendor/wasm-bindgen-futures/src/queue.rs @@ -0,0 +1,91 @@ +use js_sys::Promise; +use std::cell::{Cell, RefCell}; +use std::collections::VecDeque; +use std::rc::Rc; +use wasm_bindgen::prelude::*; + +struct QueueState { + // The queue of Tasks which are to be run in order. In practice this is all the + // synchronous work of futures, and each `Task` represents calling `poll` on + // a future "at the right time". + tasks: RefCell<VecDeque<Rc<crate::task::Task>>>, + + // This flag indicates whether we've scheduled `run_all` to run in the future. + // This is used to ensure that it's only scheduled once. + is_scheduled: Cell<bool>, +} + +impl QueueState { + fn run_all(&self) { + // "consume" the schedule + let _was_scheduled = self.is_scheduled.replace(false); + debug_assert!(_was_scheduled); + + // Stop when all tasks that have been scheduled before this tick have been run. + // Tasks that are scheduled while running tasks will run on the next tick. + let mut task_count_left = self.tasks.borrow().len(); + while task_count_left > 0 { + task_count_left -= 1; + let task = match self.tasks.borrow_mut().pop_front() { + Some(task) => task, + None => break, + }; + task.run(); + } + + // All of the Tasks have been run, so it's now possible to schedule the + // next tick again + } +} + +pub(crate) struct Queue { + state: Rc<QueueState>, + promise: Promise, + closure: Closure<dyn FnMut(JsValue)>, +} + +impl Queue { + // Schedule a task to run on the next tick + pub(crate) fn schedule_task(&self, task: Rc<crate::task::Task>) { + self.state.tasks.borrow_mut().push_back(task); + // Note that we currently use a promise and a closure to do this, but + // eventually we should probably use something like `queueMicrotask`: + // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/queueMicrotask + if !self.state.is_scheduled.replace(true) { + let _ = self.promise.then(&self.closure); + } + } + // Append a task to the currently running queue, or schedule it + pub(crate) fn push_task(&self, task: Rc<crate::task::Task>) { + // It would make sense to run this task on the same tick. For now, we + // make the simplifying choice of always scheduling tasks for a future tick. + self.schedule_task(task) + } +} + +impl Queue { + fn new() -> Self { + let state = Rc::new(QueueState { + is_scheduled: Cell::new(false), + tasks: RefCell::new(VecDeque::new()), + }); + + Self { + promise: Promise::resolve(&JsValue::undefined()), + + closure: { + let state = Rc::clone(&state); + + // This closure will only be called on the next microtask event + // tick + Closure::new(move |_| state.run_all()) + }, + + state, + } + } +} + +thread_local! { + pub(crate) static QUEUE: Queue = Queue::new(); +} diff --git a/vendor/wasm-bindgen-futures/src/stream.rs b/vendor/wasm-bindgen-futures/src/stream.rs new file mode 100644 index 000000000..fe35d81c7 --- /dev/null +++ b/vendor/wasm-bindgen-futures/src/stream.rs @@ -0,0 +1,81 @@ +//! Converting JavaScript `AsyncIterator`s to Rust `Stream`s. +//! +//! Analogous to the promise to future conversion, this module allows +//! turning objects implementing the async iterator protocol into `Stream`s +//! that produce values that can be awaited from. +//! + +use crate::JsFuture; +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use futures_core::stream::Stream; +use js_sys::{AsyncIterator, IteratorNext}; +use wasm_bindgen::prelude::*; + +/// A `Stream` that yields values from an underlying `AsyncIterator`. +pub struct JsStream { + iter: AsyncIterator, + next: Option<JsFuture>, + done: bool, +} + +impl JsStream { + fn next_future(&self) -> Result<JsFuture, JsValue> { + self.iter.next().map(JsFuture::from) + } +} + +impl From<AsyncIterator> for JsStream { + fn from(iter: AsyncIterator) -> Self { + JsStream { + iter, + next: None, + done: false, + } + } +} + +impl Stream for JsStream { + type Item = Result<JsValue, JsValue>; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> { + if self.done { + return Poll::Ready(None); + } + + let future = match self.next.as_mut() { + Some(val) => val, + None => match self.next_future() { + Ok(val) => { + self.next = Some(val); + self.next.as_mut().unwrap() + } + Err(e) => { + self.done = true; + return Poll::Ready(Some(Err(e))); + } + }, + }; + + match Pin::new(future).poll(cx) { + Poll::Ready(res) => match res { + Ok(iter_next) => { + let next = iter_next.unchecked_into::<IteratorNext>(); + if next.done() { + self.done = true; + Poll::Ready(None) + } else { + self.next.take(); + Poll::Ready(Some(Ok(next.value()))) + } + } + Err(e) => { + self.done = true; + Poll::Ready(Some(Err(e))) + } + }, + Poll::Pending => Poll::Pending, + } + } +} diff --git a/vendor/wasm-bindgen-futures/src/task/multithread.rs b/vendor/wasm-bindgen-futures/src/task/multithread.rs new file mode 100644 index 000000000..6cfdfcb54 --- /dev/null +++ b/vendor/wasm-bindgen-futures/src/task/multithread.rs @@ -0,0 +1,201 @@ +use std::cell::RefCell; +use std::future::Future; +use std::mem::ManuallyDrop; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::atomic::AtomicI32; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::Arc; +use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; +use wasm_bindgen::prelude::*; + +const SLEEPING: i32 = 0; +const AWAKE: i32 = 1; + +struct AtomicWaker { + state: AtomicI32, +} + +impl AtomicWaker { + fn new() -> Arc<Self> { + Arc::new(Self { + state: AtomicI32::new(AWAKE), + }) + } + + fn wake_by_ref(&self) { + // If we're already AWAKE then we previously notified and there's + // nothing to do... + match self.state.swap(AWAKE, SeqCst) { + AWAKE => return, + other => debug_assert_eq!(other, SLEEPING), + } + + // ... otherwise we execute the native `notify` instruction to wake up + // the corresponding `waitAsync` that was waiting for the transition + // from SLEEPING to AWAKE. + unsafe { + core::arch::wasm32::memory_atomic_notify( + &self.state as *const AtomicI32 as *mut i32, + 1, // Number of threads to notify + ); + } + } + + /// Same as the singlethread module, this creates a standard library + /// `RawWaker`. We could use `futures_util::task::ArcWake` but it's small + /// enough that we just inline it for now. + unsafe fn into_raw_waker(this: Arc<Self>) -> RawWaker { + unsafe fn raw_clone(ptr: *const ()) -> RawWaker { + let ptr = ManuallyDrop::new(Arc::from_raw(ptr as *const AtomicWaker)); + AtomicWaker::into_raw_waker((*ptr).clone()) + } + + unsafe fn raw_wake(ptr: *const ()) { + let ptr = Arc::from_raw(ptr as *const AtomicWaker); + AtomicWaker::wake_by_ref(&ptr); + } + + unsafe fn raw_wake_by_ref(ptr: *const ()) { + let ptr = ManuallyDrop::new(Arc::from_raw(ptr as *const AtomicWaker)); + AtomicWaker::wake_by_ref(&ptr); + } + + unsafe fn raw_drop(ptr: *const ()) { + drop(Arc::from_raw(ptr as *const AtomicWaker)); + } + + const VTABLE: RawWakerVTable = + RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop); + + RawWaker::new(Arc::into_raw(this) as *const (), &VTABLE) + } +} + +struct Inner { + future: Pin<Box<dyn Future<Output = ()> + 'static>>, + closure: Closure<dyn FnMut(JsValue)>, +} + +pub(crate) struct Task { + atomic: Arc<AtomicWaker>, + waker: Waker, + // See `singlethread.rs` for why this is an internal `Option`. + inner: RefCell<Option<Inner>>, +} + +impl Task { + pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()> + 'static>>) { + let atomic = AtomicWaker::new(); + let waker = unsafe { Waker::from_raw(AtomicWaker::into_raw_waker(atomic.clone())) }; + let this = Rc::new(Task { + atomic, + waker, + inner: RefCell::new(None), + }); + + let closure = { + let this = Rc::clone(&this); + Closure::new(move |_| this.run()) + }; + *this.inner.borrow_mut() = Some(Inner { future, closure }); + + // Queue up the Future's work to happen on the next microtask tick. + crate::queue::QUEUE.with(move |queue| queue.schedule_task(this)); + } + + pub(crate) fn run(&self) { + let mut borrow = self.inner.borrow_mut(); + + // Same as `singlethread.rs`, handle spurious wakeups happening after we + // finished. + let inner = match borrow.as_mut() { + Some(inner) => inner, + None => return, + }; + + loop { + // Also the same as `singlethread.rs`, flag ourselves as ready to + // receive a notification. + let prev = self.atomic.state.swap(SLEEPING, SeqCst); + debug_assert_eq!(prev, AWAKE); + + let poll = { + let mut cx = Context::from_waker(&self.waker); + inner.future.as_mut().poll(&mut cx) + }; + + match poll { + // Same as `singlethread.rs` (noticing a pattern?) clean up + // resources associated with the future ASAP. + Poll::Ready(()) => { + *borrow = None; + } + + // Unlike `singlethread.rs` we are responsible for ensuring there's + // a closure to handle the notification that a Future is ready. In + // the single-threaded case the notification itself enqueues work, + // but in the multithreaded case we don't know what thread a + // notification comes from so we need to ensure the current running + // thread is the one that enqueues the work. To do that we execute + // `Atomics.waitAsync`, creating a local Promise on our own thread + // which will resolve once `Atomics.notify` is called. + // + // We could be in one of two states as we execute this: + // + // * `SLEEPING` - we'll get notified via `Atomics.notify` + // and then this Promise will resolve. + // + // * `AWAKE` - the Promise will immediately be resolved and + // we'll execute the work on the next microtask queue. + Poll::Pending => { + match wait_async(&self.atomic.state, SLEEPING) { + Some(promise) => drop(promise.then(&inner.closure)), + // our state has already changed so we can just do the work + // again inline. + None => continue, + } + } + } + break; + } + } +} + +fn wait_async(ptr: &AtomicI32, current_value: i32) -> Option<js_sys::Promise> { + // If `Atomics.waitAsync` isn't defined then we use our fallback, otherwise + // we use the native function. + return if Atomics::get_wait_async().is_undefined() { + Some(crate::task::wait_async_polyfill::wait_async( + ptr, + current_value, + )) + } else { + let mem = wasm_bindgen::memory().unchecked_into::<js_sys::WebAssembly::Memory>(); + let array = js_sys::Int32Array::new(&mem.buffer()); + let result = Atomics::wait_async(&array, ptr as *const AtomicI32 as i32 / 4, current_value); + if result.async_() { + Some(result.value()) + } else { + None + } + }; + + #[wasm_bindgen] + extern "C" { + type Atomics; + type WaitAsyncResult; + + #[wasm_bindgen(static_method_of = Atomics, js_name = waitAsync)] + fn wait_async(buf: &js_sys::Int32Array, index: i32, value: i32) -> WaitAsyncResult; + + #[wasm_bindgen(static_method_of = Atomics, js_name = waitAsync, getter)] + fn get_wait_async() -> JsValue; + + #[wasm_bindgen(method, getter, structural, js_name = async)] + fn async_(this: &WaitAsyncResult) -> bool; + + #[wasm_bindgen(method, getter, structural)] + fn value(this: &WaitAsyncResult) -> js_sys::Promise; + } +} diff --git a/vendor/wasm-bindgen-futures/src/task/singlethread.rs b/vendor/wasm-bindgen-futures/src/task/singlethread.rs new file mode 100644 index 000000000..570cd6fcf --- /dev/null +++ b/vendor/wasm-bindgen-futures/src/task/singlethread.rs @@ -0,0 +1,113 @@ +use std::cell::{Cell, RefCell}; +use std::future::Future; +use std::mem::ManuallyDrop; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + +struct Inner { + future: Pin<Box<dyn Future<Output = ()> + 'static>>, + waker: Waker, +} + +pub(crate) struct Task { + // The actual Future that we're executing as part of this task. + // + // This is an Option so that the Future can be immediately dropped when it's + // finished + inner: RefCell<Option<Inner>>, + + // This is used to ensure that the Task will only be queued once + is_queued: Cell<bool>, +} + +impl Task { + pub(crate) fn spawn(future: Pin<Box<dyn Future<Output = ()> + 'static>>) { + let this = Rc::new(Self { + inner: RefCell::new(None), + is_queued: Cell::new(true), + }); + + let waker = unsafe { Waker::from_raw(Task::into_raw_waker(Rc::clone(&this))) }; + + *this.inner.borrow_mut() = Some(Inner { future, waker }); + + crate::queue::QUEUE.with(|queue| queue.schedule_task(this)); + } + + fn wake_by_ref(this: &Rc<Self>) { + // If we've already been placed on the run queue then there's no need to + // requeue ourselves since we're going to run at some point in the + // future anyway. + if this.is_queued.replace(true) { + return; + } + + crate::queue::QUEUE.with(|queue| { + queue.push_task(Rc::clone(this)); + }); + } + + /// Creates a standard library `RawWaker` from an `Rc` of ourselves. + /// + /// Note that in general this is wildly unsafe because everything with + /// Futures requires `Sync` + `Send` with regard to Wakers. For wasm, + /// however, everything is guaranteed to be singlethreaded (since we're + /// compiled without the `atomics` feature) so we "safely lie" and say our + /// `Rc` pointer is good enough. + unsafe fn into_raw_waker(this: Rc<Self>) -> RawWaker { + unsafe fn raw_clone(ptr: *const ()) -> RawWaker { + let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task)); + Task::into_raw_waker((*ptr).clone()) + } + + unsafe fn raw_wake(ptr: *const ()) { + let ptr = Rc::from_raw(ptr as *const Task); + Task::wake_by_ref(&ptr); + } + + unsafe fn raw_wake_by_ref(ptr: *const ()) { + let ptr = ManuallyDrop::new(Rc::from_raw(ptr as *const Task)); + Task::wake_by_ref(&ptr); + } + + unsafe fn raw_drop(ptr: *const ()) { + drop(Rc::from_raw(ptr as *const Task)); + } + + const VTABLE: RawWakerVTable = + RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop); + + RawWaker::new(Rc::into_raw(this) as *const (), &VTABLE) + } + + pub(crate) fn run(&self) { + let mut borrow = self.inner.borrow_mut(); + + // Wakeups can come in after a Future has finished and been destroyed, + // so handle this gracefully by just ignoring the request to run. + let inner = match borrow.as_mut() { + Some(inner) => inner, + None => return, + }; + + // Ensure that if poll calls `waker.wake()` we can get enqueued back on + // the run queue. + self.is_queued.set(false); + + let poll = { + let mut cx = Context::from_waker(&inner.waker); + inner.future.as_mut().poll(&mut cx) + }; + + // If a future has finished (`Ready`) then clean up resources associated + // with the future ASAP. This ensures that we don't keep anything extra + // alive in-memory by accident. Our own struct, `Rc<Task>` won't + // actually go away until all wakers referencing us go away, which may + // take quite some time, so ensure that the heaviest of resources are + // released early. + if let Poll::Ready(_) = poll { + *borrow = None; + } + } +} diff --git a/vendor/wasm-bindgen-futures/src/task/wait_async_polyfill.rs b/vendor/wasm-bindgen-futures/src/task/wait_async_polyfill.rs new file mode 100644 index 000000000..68332d91a --- /dev/null +++ b/vendor/wasm-bindgen-futures/src/task/wait_async_polyfill.rs @@ -0,0 +1,91 @@ +//! +//! The polyfill was kindly borrowed from https://github.com/tc39/proposal-atomics-wait-async +//! and ported to Rust +//! + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Author: Lars T Hansen, lhansen@mozilla.com + */ + +/* Polyfill for Atomics.waitAsync() for web browsers. + * + * Any kind of agent that is able to create a new Worker can use this polyfill. + * + * Load this file in all agents that will use Atomics.waitAsync. + * + * Agents that don't call Atomics.waitAsync need do nothing special. + * + * Any kind of agent can wake another agent that is sleeping in + * Atomics.waitAsync by just calling Atomics.wake for the location being slept + * on, as normal. + * + * The implementation is not completely faithful to the proposed semantics: in + * the case where an agent first asyncWaits and then waits on the same location: + * when it is woken, the two waits will be woken in order, while in the real + * semantics, the sync wait will be woken first. + * + * In this polyfill Atomics.waitAsync is not very fast. + */ + +/* Implementation: + * + * For every wait we fork off a Worker to perform the wait. Workers are reused + * when possible. The worker communicates with its parent using postMessage. + */ + +use js_sys::{Array, Promise}; +use std::cell::RefCell; +use std::sync::atomic::AtomicI32; +use wasm_bindgen::prelude::*; +use web_sys::{MessageEvent, Worker}; + +thread_local! { + static HELPERS: RefCell<Vec<Worker>> = RefCell::new(vec![]); +} + +fn alloc_helper() -> Worker { + HELPERS.with(|helpers| { + if let Some(helper) = helpers.borrow_mut().pop() { + return helper; + } + + let worker_url = wasm_bindgen::link_to!(module = "/src/task/worker.js"); + Worker::new(&worker_url).unwrap_or_else(|js| wasm_bindgen::throw_val(js)) + }) +} + +fn free_helper(helper: Worker) { + HELPERS.with(move |helpers| { + let mut helpers = helpers.borrow_mut(); + helpers.push(helper.clone()); + helpers.truncate(10); // random arbitrary limit chosen here + }); +} + +pub fn wait_async(ptr: &AtomicI32, value: i32) -> Promise { + Promise::new(&mut |resolve, _reject| { + let helper = alloc_helper(); + let helper_ref = helper.clone(); + + let onmessage_callback = Closure::once_into_js(move |e: MessageEvent| { + // Our helper is done waiting so it's available to wait on a + // different location, so return it to the free list. + free_helper(helper_ref); + drop(resolve.call1(&JsValue::NULL, &e.data())); + }); + helper.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref())); + + let data = Array::of3( + &wasm_bindgen::memory(), + &JsValue::from(ptr as *const AtomicI32 as i32 / 4), + &JsValue::from(value), + ); + + helper + .post_message(&data) + .unwrap_or_else(|js| wasm_bindgen::throw_val(js)); + }) +} diff --git a/vendor/wasm-bindgen-futures/src/task/worker.js b/vendor/wasm-bindgen-futures/src/task/worker.js new file mode 100644 index 000000000..d25dab660 --- /dev/null +++ b/vendor/wasm-bindgen-futures/src/task/worker.js @@ -0,0 +1,6 @@ +onmessage = function (ev) { + let [ia, index, value] = ev.data; + ia = new Int32Array(ia.buffer); + let result = Atomics.wait(ia, index, value); + postMessage(result); +}; diff --git a/vendor/wasm-bindgen-futures/tests/tests.rs b/vendor/wasm-bindgen-futures/tests/tests.rs new file mode 100644 index 000000000..6a950f7c8 --- /dev/null +++ b/vendor/wasm-bindgen-futures/tests/tests.rs @@ -0,0 +1,157 @@ +#![cfg(target_arch = "wasm32")] + +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +use futures_channel::oneshot; +use js_sys::Promise; +use std::ops::FnMut; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::{future_to_promise, spawn_local, JsFuture}; +use wasm_bindgen_test::*; + +#[wasm_bindgen_test] +async fn promise_resolve_is_ok_future() { + let p = js_sys::Promise::resolve(&JsValue::from(42)); + let x = JsFuture::from(p).await.unwrap(); + assert_eq!(x, 42); +} + +#[wasm_bindgen_test] +async fn promise_reject_is_error_future() { + let p = js_sys::Promise::reject(&JsValue::from(42)); + let e = JsFuture::from(p).await.unwrap_err(); + assert_eq!(e, 42); +} + +#[wasm_bindgen_test] +async fn ok_future_is_resolved_promise() { + let p = future_to_promise(async { Ok(JsValue::from(42)) }); + let x = JsFuture::from(p).await.unwrap(); + assert_eq!(x, 42); +} + +#[wasm_bindgen_test] +async fn error_future_is_rejected_promise() { + let p = future_to_promise(async { Err(JsValue::from(42)) }); + let e = JsFuture::from(p).await.unwrap_err(); + assert_eq!(e, 42); +} + +#[wasm_bindgen_test] +fn debug_jsfuture() { + let p = js_sys::Promise::resolve(&JsValue::from(42)); + let f = JsFuture::from(p); + assert_eq!(&format!("{:?}", f), "JsFuture { ... }"); +} + +#[wasm_bindgen] +extern "C" { + fn setTimeout(c: &Closure<dyn FnMut()>); +} + +#[wasm_bindgen_test] +async fn oneshot_works() { + let (tx, rx) = oneshot::channel::<u32>(); + let mut tx = Some(tx); + let closure = Closure::wrap(Box::new(move || { + drop(tx.take().unwrap()); + }) as Box<dyn FnMut()>); + setTimeout(&closure); + closure.forget(); + rx.await.unwrap_err(); +} + +#[wasm_bindgen_test] +async fn spawn_local_runs() { + let (tx, rx) = oneshot::channel::<u32>(); + spawn_local(async { + tx.send(42).unwrap(); + }); + assert_eq!(rx.await.unwrap(), 42); +} + +#[wasm_bindgen_test] +async fn spawn_local_nested() { + let (ta, mut ra) = oneshot::channel::<u32>(); + let (ts, rs) = oneshot::channel::<u32>(); + let (tx, rx) = oneshot::channel::<u32>(); + // The order in which the various promises and tasks run is important! + // We want, on different ticks each, the following things to happen + // 1. A promise resolves, off of which we can spawn our inbetween assertion + // 2. The outer task runs, spawns in the inner task, and the inbetween promise, then yields + // 3. The inbetween promise runs and asserts that the inner task hasn't run + // 4. The inner task runs + // This depends crucially on two facts: + // - JsFuture schedules on ticks independently from tasks + // - The order of ticks is the same as the code flow + let promise = Promise::resolve(&JsValue::null()); + + spawn_local(async move { + // Create a closure that runs in between the two ticks and + // assert that the inner task hasn't run yet + let inbetween = Closure::wrap(Box::new(move |_| { + assert_eq!( + ra.try_recv().unwrap(), + None, + "Nested task should not have run yet" + ); + }) as Box<dyn FnMut(JsValue)>); + let inbetween = promise.then(&inbetween); + spawn_local(async { + ta.send(0xdead).unwrap(); + ts.send(0xbeaf).unwrap(); + }); + JsFuture::from(inbetween).await.unwrap(); + assert_eq!( + rs.await.unwrap(), + 0xbeaf, + "Nested task should run eventually" + ); + tx.send(42).unwrap(); + }); + + assert_eq!(rx.await.unwrap(), 42); +} + +#[wasm_bindgen_test] +async fn spawn_local_err_no_exception() { + let (tx, rx) = oneshot::channel::<u32>(); + spawn_local(async {}); + spawn_local(async { + tx.send(42).unwrap(); + }); + let val = rx.await.unwrap(); + assert_eq!(val, 42); +} + +#[wasm_bindgen_test] +async fn can_create_multiple_futures_from_same_promise() { + let promise = js_sys::Promise::resolve(&JsValue::null()); + let a = JsFuture::from(promise.clone()); + let b = JsFuture::from(promise); + + a.await.unwrap(); + b.await.unwrap(); +} + +#[cfg(feature = "futures-core-03-stream")] +#[wasm_bindgen_test] +async fn can_use_an_async_iterable_as_stream() { + use futures_lite::stream::StreamExt; + use wasm_bindgen_futures::stream::JsStream; + + let async_iter = js_sys::Function::new_no_args( + "return async function*() { + yield 42; + yield 24; + }()", + ) + .call0(&JsValue::undefined()) + .unwrap() + .unchecked_into::<js_sys::AsyncIterator>(); + + let mut stream = JsStream::from(async_iter); + assert_eq!(stream.next().await, Some(Ok(JsValue::from(42)))); + assert_eq!(stream.next().await, Some(Ok(JsValue::from(24)))); + assert_eq!(stream.next().await, None); +} |