|
1 | | -use llrt_utils::{ |
2 | | - module::{export_default, ModuleInfo}, |
3 | | - option::Undefined, |
4 | | - primordials::Primordial, |
5 | | -}; |
6 | | -use queuing_strategy::{ByteLengthQueuingStrategy, CountQueuingStrategy, SizeValue}; |
| 1 | +use llrt_utils::module::{export_default, ModuleInfo}; |
| 2 | +use queuing_strategy::{ByteLengthQueuingStrategy, CountQueuingStrategy}; |
7 | 3 | use readable::{ |
8 | 4 | ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader, |
9 | | - ReadableStreamBYOBRequest, ReadableStreamClass, ReadableStreamDefaultController, |
10 | | - ReadableStreamDefaultReader, |
| 5 | + ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, |
11 | 6 | }; |
12 | 7 | use rquickjs::{ |
13 | | - atom::PredefinedAtom, |
14 | | - class::{JsClass, OwnedBorrowMut, Trace, Tracer}, |
15 | | - function::Constructor, |
16 | 8 | module::{Declarations, Exports, ModuleDef}, |
17 | | - prelude::{IntoArg, OnceFn, This}, |
18 | | - promise::PromiseState, |
19 | | - Class, Ctx, Error, Exception, FromJs, Function, IntoAtom, IntoJs, JsLifetime, Object, Promise, |
20 | | - Result, Value, |
21 | | -}; |
22 | | -use std::collections::VecDeque; |
23 | | -use std::{cell::Cell, rc::Rc}; |
24 | | -use writable::{ |
25 | | - WritableStream, WritableStreamClass, WritableStreamDefaultController, |
26 | | - WritableStreamDefaultWriter, |
| 9 | + Class, Ctx, Result, |
27 | 10 | }; |
| 11 | +use writable::{WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter}; |
28 | 12 |
|
29 | 13 | mod queuing_strategy; |
30 | 14 | mod readable; |
| 15 | +mod readable_writable_pair; |
| 16 | +mod utils; |
31 | 17 | mod writable; |
32 | 18 |
|
33 | | -struct ReadableWritablePair<'js> { |
34 | | - readable: ReadableStreamClass<'js>, |
35 | | - writable: WritableStreamClass<'js>, |
36 | | -} |
37 | | - |
38 | | -impl<'js> FromJs<'js> for ReadableWritablePair<'js> { |
39 | | - fn from_js(_ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> { |
40 | | - let ty_name = value.type_name(); |
41 | | - let obj = value |
42 | | - .as_object() |
43 | | - .ok_or(Error::new_from_js(ty_name, "Object"))?; |
44 | | - |
45 | | - let readable = obj.get::<_, ReadableStreamClass<'js>>("readable")?; |
46 | | - let writable = obj.get::<_, Class<'js, WritableStream>>("writable")?; |
47 | | - |
48 | | - Ok(Self { readable, writable }) |
49 | | - } |
50 | | -} |
51 | | - |
52 | 19 | pub struct StreamWebModule; |
53 | 20 |
|
54 | 21 | // https://nodejs.org/api/webstreams.html |
@@ -122,317 +89,3 @@ pub fn init(ctx: &Ctx) -> Result<()> { |
122 | 89 |
|
123 | 90 | Ok(()) |
124 | 91 | } |
125 | | - |
126 | | -fn class_from_owned_borrow_mut<'js, T: JsClass<'js>>( |
127 | | - borrow: OwnedBorrowMut<'js, T>, |
128 | | -) -> (Class<'js, T>, OwnedBorrowMut<'js, T>) { |
129 | | - let class = borrow.into_inner(); |
130 | | - let borrow = OwnedBorrowMut::from_class(class.clone()); |
131 | | - (class, borrow) |
132 | | -} |
133 | | - |
134 | | -// the trait used elsewhere in this repo accepts null values as 'None', which causes many web platform tests to fail as they |
135 | | -// like to check that undefined is accepted and null isn't. |
136 | | -trait ValueOrUndefined<'js> { |
137 | | - fn get_value_or_undefined<K: IntoAtom<'js> + Clone, V: FromJs<'js>>( |
138 | | - &self, |
139 | | - k: K, |
140 | | - ) -> Result<Option<V>>; |
141 | | -} |
142 | | - |
143 | | -impl<'js> ValueOrUndefined<'js> for Object<'js> { |
144 | | - fn get_value_or_undefined<K: IntoAtom<'js> + Clone, V: FromJs<'js> + Sized>( |
145 | | - &self, |
146 | | - k: K, |
147 | | - ) -> Result<Option<V>> { |
148 | | - let value = self.get::<K, Value<'js>>(k)?; |
149 | | - Ok(Undefined::from_js(self.ctx(), value)?.0) |
150 | | - } |
151 | | -} |
152 | | - |
153 | | -impl<'js> ValueOrUndefined<'js> for Value<'js> { |
154 | | - fn get_value_or_undefined<K: IntoAtom<'js> + Clone, V: FromJs<'js>>( |
155 | | - &self, |
156 | | - k: K, |
157 | | - ) -> Result<Option<V>> { |
158 | | - if let Some(obj) = self.as_object() { |
159 | | - return obj.get_value_or_undefined(k); |
160 | | - } |
161 | | - Ok(None) |
162 | | - } |
163 | | -} |
164 | | - |
165 | | -trait UnwrapOrUndefined<'js> { |
166 | | - fn unwrap_or_undefined(self, ctx: &Ctx<'js>) -> Value<'js>; |
167 | | -} |
168 | | - |
169 | | -impl<'js> UnwrapOrUndefined<'js> for Option<Value<'js>> { |
170 | | - fn unwrap_or_undefined(self, ctx: &Ctx<'js>) -> Value<'js> { |
171 | | - self.unwrap_or_else(|| Value::new_undefined(ctx.clone())) |
172 | | - } |
173 | | -} |
174 | | - |
175 | | -fn promise_rejected_with<'js>( |
176 | | - primordials: &PromisePrimordials<'js>, |
177 | | - value: Value<'js>, |
178 | | -) -> Result<Promise<'js>> { |
179 | | - primordials |
180 | | - .promise_reject |
181 | | - .call((This(primordials.promise_constructor.clone()), value)) |
182 | | -} |
183 | | - |
184 | | -fn promise_resolved_with<'js>( |
185 | | - ctx: &Ctx<'js>, |
186 | | - primordials: &PromisePrimordials<'js>, |
187 | | - value: Result<Value<'js>>, |
188 | | -) -> Result<Promise<'js>> { |
189 | | - match value { |
190 | | - Ok(value) => primordials |
191 | | - .promise_resolve |
192 | | - .call((This(primordials.promise_constructor.clone()), value)), |
193 | | - Err(Error::Exception) => primordials |
194 | | - .promise_reject |
195 | | - .call((This(primordials.promise_constructor.clone()), ctx.catch())), |
196 | | - Err(err) => Err(err), |
197 | | - } |
198 | | -} |
199 | | - |
200 | | -#[derive(JsLifetime, Clone)] |
201 | | -struct PromisePrimordials<'js> { |
202 | | - promise_constructor: Constructor<'js>, |
203 | | - promise_resolve: Function<'js>, |
204 | | - promise_reject: Function<'js>, |
205 | | - promise_all: Function<'js>, |
206 | | - promise_resolved_with_undefined: Promise<'js>, |
207 | | -} |
208 | | - |
209 | | -impl<'js> Primordial<'js> for PromisePrimordials<'js> { |
210 | | - fn new(ctx: &Ctx<'js>) -> Result<Self> |
211 | | - where |
212 | | - Self: Sized, |
213 | | - { |
214 | | - let promise_constructor: Constructor<'js> = ctx.globals().get(PredefinedAtom::Promise)?; |
215 | | - let promise_resolve: Function<'js> = promise_constructor.get("resolve")?; |
216 | | - let promise_reject: Function<'js> = promise_constructor.get("reject")?; |
217 | | - let promise_all: Function<'js> = promise_constructor.get("all")?; |
218 | | - |
219 | | - let promise_resolved_with_undefined = promise_resolve.call(( |
220 | | - This(promise_constructor.clone()), |
221 | | - Value::new_undefined(ctx.clone()), |
222 | | - ))?; |
223 | | - |
224 | | - Ok(Self { |
225 | | - promise_constructor, |
226 | | - promise_resolve, |
227 | | - promise_reject, |
228 | | - promise_all, |
229 | | - promise_resolved_with_undefined, |
230 | | - }) |
231 | | - } |
232 | | -} |
233 | | - |
234 | | -// https://webidl.spec.whatwg.org/#dfn-perform-steps-once-promise-is-settled |
235 | | -fn upon_promise<'js, Input: FromJs<'js> + 'js, Output: IntoJs<'js> + 'js>( |
236 | | - ctx: Ctx<'js>, |
237 | | - promise: Promise<'js>, |
238 | | - then: impl FnOnce(Ctx<'js>, std::result::Result<Input, Value<'js>>) -> Result<Output> + 'js, |
239 | | -) -> Result<Promise<'js>> { |
240 | | - let then = Rc::new(Cell::new(Some(then))); |
241 | | - let then2 = then.clone(); |
242 | | - promise.then()?.call(( |
243 | | - This(promise.clone()), |
244 | | - Function::new( |
245 | | - ctx.clone(), |
246 | | - OnceFn::new(move |ctx, input| { |
247 | | - then.take() |
248 | | - .expect("Promise.then should only call either resolve or reject")( |
249 | | - ctx, |
250 | | - Ok(input), |
251 | | - ) |
252 | | - }), |
253 | | - ), |
254 | | - Function::new( |
255 | | - ctx, |
256 | | - OnceFn::new(move |ctx, e: Value<'js>| { |
257 | | - then2 |
258 | | - .take() |
259 | | - .expect("Promise.then should only call either resolve or reject")( |
260 | | - ctx, Err(e) |
261 | | - ) |
262 | | - }), |
263 | | - ), |
264 | | - )) |
265 | | -} |
266 | | - |
267 | | -fn upon_promise_fulfilment<'js, Input: FromJs<'js> + 'js, Output: IntoJs<'js> + 'js>( |
268 | | - ctx: Ctx<'js>, |
269 | | - promise: Promise<'js>, |
270 | | - then: impl FnOnce(Ctx<'js>, Input) -> Result<Output> + 'js, |
271 | | -) -> Result<Promise<'js>> { |
272 | | - promise.then()?.call(( |
273 | | - This(promise.clone()), |
274 | | - Function::new(ctx.clone(), OnceFn::new(then)), |
275 | | - )) |
276 | | -} |
277 | | - |
278 | | -fn is_non_negative_number(value: SizeValue<'_>) -> Option<f64> { |
279 | | - // If Type(v) is not Number, return false. |
280 | | - let number = value.as_number()?; |
281 | | - // If v is NaN, return false. |
282 | | - if number.is_nan() { |
283 | | - return None; |
284 | | - } |
285 | | - |
286 | | - // If v < 0, return false. |
287 | | - if number < 0.0 { |
288 | | - return None; |
289 | | - } |
290 | | - |
291 | | - // Return true. |
292 | | - Some(number) |
293 | | -} |
294 | | - |
295 | | -#[derive(JsLifetime, Trace, Clone)] |
296 | | -struct ValueWithSize<'js> { |
297 | | - value: Value<'js>, |
298 | | - size: f64, |
299 | | -} |
300 | | - |
301 | | -#[derive(Debug, JsLifetime, Clone)] |
302 | | -struct ResolveablePromise<'js> { |
303 | | - promise: Promise<'js>, |
304 | | - resolve: Option<Function<'js>>, |
305 | | - reject: Option<Function<'js>>, |
306 | | -} |
307 | | - |
308 | | -impl<'js> ResolveablePromise<'js> { |
309 | | - fn new(ctx: &Ctx<'js>) -> Result<Self> { |
310 | | - let (promise, resolve, reject) = Promise::new(ctx)?; |
311 | | - Ok(Self { |
312 | | - promise, |
313 | | - resolve: Some(resolve), |
314 | | - reject: Some(reject), |
315 | | - }) |
316 | | - } |
317 | | - |
318 | | - fn resolved_with_undefined(primordials: &PromisePrimordials<'js>) -> Self { |
319 | | - Self { |
320 | | - promise: primordials.promise_resolved_with_undefined.clone(), |
321 | | - resolve: None, |
322 | | - reject: None, |
323 | | - } |
324 | | - } |
325 | | - |
326 | | - fn rejected_with(primordials: &PromisePrimordials<'js>, error: Value<'js>) -> Result<Self> { |
327 | | - Ok(Self { |
328 | | - promise: promise_rejected_with(primordials, error)?, |
329 | | - resolve: None, |
330 | | - reject: None, |
331 | | - }) |
332 | | - } |
333 | | - |
334 | | - fn resolve(&self, value: impl IntoArg<'js>) -> Result<()> { |
335 | | - if let Some(resolve) = &self.resolve { |
336 | | - let () = resolve.call((value,))?; |
337 | | - } |
338 | | - Ok(()) |
339 | | - } |
340 | | - |
341 | | - fn resolve_undefined(&self) -> Result<()> { |
342 | | - if let Some(resolve) = &self.resolve { |
343 | | - let () = resolve.call((rquickjs::Undefined,))?; |
344 | | - } |
345 | | - Ok(()) |
346 | | - } |
347 | | - |
348 | | - fn reject(&self, value: impl IntoArg<'js>) -> Result<()> { |
349 | | - if let Some(reject) = &self.reject { |
350 | | - let () = reject.call((value,))?; |
351 | | - } |
352 | | - Ok(()) |
353 | | - } |
354 | | - |
355 | | - fn is_pending(&self) -> bool { |
356 | | - self.promise.state() == PromiseState::Pending |
357 | | - } |
358 | | - |
359 | | - fn set_is_handled(&self) -> Result<()> { |
360 | | - self.promise.catch()?.call(( |
361 | | - This(self.promise.clone()), |
362 | | - Function::new(self.promise.ctx().clone(), || {}), |
363 | | - )) |
364 | | - } |
365 | | -} |
366 | | - |
367 | | -impl<'js> Trace<'js> for ResolveablePromise<'js> { |
368 | | - fn trace<'a>(&self, tracer: Tracer<'a, 'js>) { |
369 | | - self.promise.trace(tracer); |
370 | | - self.resolve.trace(tracer); |
371 | | - self.reject.trace(tracer); |
372 | | - } |
373 | | -} |
374 | | - |
375 | | -#[derive(JsLifetime, Trace)] |
376 | | -struct Container<'js> { |
377 | | - queue: VecDeque<ValueWithSize<'js>>, |
378 | | - queue_total_size: f64, |
379 | | -} |
380 | | - |
381 | | -impl<'js> Container<'js> { |
382 | | - fn new() -> Self { |
383 | | - Self { |
384 | | - queue: VecDeque::new(), |
385 | | - queue_total_size: 0.0, |
386 | | - } |
387 | | - } |
388 | | - |
389 | | - fn enqueue_value_with_size( |
390 | | - &mut self, |
391 | | - ctx: &Ctx<'js>, |
392 | | - value: Value<'js>, |
393 | | - size: SizeValue<'js>, |
394 | | - ) -> Result<()> { |
395 | | - let size = match is_non_negative_number(size) { |
396 | | - None => { |
397 | | - // If ! IsNonNegativeNumber(size) is false, throw a RangeError exception. |
398 | | - return Err(Exception::throw_range( |
399 | | - ctx, |
400 | | - "Size must be a finite, non-NaN, non-negative number.", |
401 | | - )); |
402 | | - }, |
403 | | - Some(size) => size, |
404 | | - }; |
405 | | - |
406 | | - // If size is +∞, throw a RangeError exception. |
407 | | - if size.is_infinite() { |
408 | | - return Err(Exception::throw_range( |
409 | | - ctx, |
410 | | - "Size must be a finite, non-NaN, non-negative number.", |
411 | | - )); |
412 | | - }; |
413 | | - |
414 | | - // Append a new value-with-size with value value and size size to container.[[queue]]. |
415 | | - self.queue.push_back(ValueWithSize { value, size }); |
416 | | - |
417 | | - // Set container.[[queueTotalSize]] to container.[[queueTotalSize]] + size. |
418 | | - self.queue_total_size += size; |
419 | | - |
420 | | - Ok(()) |
421 | | - } |
422 | | - |
423 | | - fn dequeue_value(&mut self) -> Value<'js> { |
424 | | - // Let valueWithSize be container.[[queue]][0]. |
425 | | - // Remove valueWithSize from container.[[queue]]. |
426 | | - let value_with_size = self |
427 | | - .queue |
428 | | - .pop_front() |
429 | | - .expect("DequeueValue called with empty queue"); |
430 | | - // Set container.[[queueTotalSize]] to container.[[queueTotalSize]] − valueWithSize’s size. |
431 | | - self.queue_total_size -= value_with_size.size; |
432 | | - // If container.[[queueTotalSize]] < 0, set container.[[queueTotalSize]] to 0. (This can occur due to rounding errors.) |
433 | | - if self.queue_total_size < 0.0 { |
434 | | - self.queue_total_size = 0.0 |
435 | | - } |
436 | | - value_with_size.value |
437 | | - } |
438 | | -} |
0 commit comments