Skip to content

Commit 329c5b0

Browse files
committed
Move helpers from lib into our own utils
1 parent f23d14c commit 329c5b0

File tree

18 files changed

+458
-403
lines changed

18 files changed

+458
-403
lines changed

modules/llrt_stream_web/src/lib.rs

Lines changed: 7 additions & 354 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,21 @@
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};
73
use readable::{
84
ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader,
9-
ReadableStreamBYOBRequest, ReadableStreamClass, ReadableStreamDefaultController,
10-
ReadableStreamDefaultReader,
5+
ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader,
116
};
127
use rquickjs::{
13-
atom::PredefinedAtom,
14-
class::{JsClass, OwnedBorrowMut, Trace, Tracer},
15-
function::Constructor,
168
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,
2710
};
11+
use writable::{WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter};
2812

2913
mod queuing_strategy;
3014
mod readable;
15+
mod readable_writable_pair;
16+
mod utils;
3117
mod writable;
3218

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-
5219
pub struct StreamWebModule;
5320

5421
// https://nodejs.org/api/webstreams.html
@@ -122,317 +89,3 @@ pub fn init(ctx: &Ctx) -> Result<()> {
12289

12390
Ok(())
12491
}
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-
}

modules/llrt_stream_web/src/queuing_strategy/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use rquickjs::{
44
Class, Ctx, Error, Exception, FromJs, Function, JsLifetime, Object, Result, Value,
55
};
66

7-
use super::ValueOrUndefined;
8-
97
pub(crate) use byte_length::ByteLengthQueuingStrategy;
108
pub(crate) use count::CountQueuingStrategy;
119

10+
use crate::utils::ValueOrUndefined;
11+
1212
mod byte_length;
1313
mod count;
1414

0 commit comments

Comments
 (0)