Skip to main content

ferrijs_std/utils/
class.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3use rquickjs::{
4    atom::PredefinedAtom, class::JsClass, object::Accessor, object::Property, prelude::This, Array,
5    Class, Ctx, Function, Object, Result, Symbol, Value,
6};
7
8use super::{
9    object::ObjectExt,
10    primordials::{BasePrimordials, Primordial},
11    result::OptionExt,
12};
13
14pub static CUSTOM_INSPECT_SYMBOL_DESCRIPTION: &str = "llrt.inspect.custom";
15
16/// Which view an iterator yields: `keys()`, `values()`, or `entries()`.
17#[derive(Clone, Copy)]
18pub enum IterKind {
19    Keys,
20    Values,
21    Entries,
22}
23
24/// Wrap an entry into a `{ value, done }` iterator result. `None` means done.
25pub fn iterator_result<'js>(
26    ctx: &Ctx<'js>,
27    kind: IterKind,
28    entry: Option<(Value<'js>, Value<'js>)>,
29) -> Result<Object<'js>> {
30    let obj = Object::new(ctx.clone())?;
31    match entry {
32        Some((key, value)) => {
33            obj.set(PredefinedAtom::Done, false)?;
34            match kind {
35                IterKind::Keys => obj.set(PredefinedAtom::Value, key)?,
36                IterKind::Values => obj.set(PredefinedAtom::Value, value)?,
37                IterKind::Entries => {
38                    let entry = Array::new(ctx.clone())?;
39                    entry.set(0, key)?;
40                    entry.set(1, value)?;
41                    obj.set(PredefinedAtom::Value, entry)?;
42                },
43            }
44        },
45        None => obj.set(PredefinedAtom::Done, true)?,
46    }
47    Ok(obj)
48}
49
50/// Create a WebIDL iterator instance, wiring its prototype the first time:
51/// the prototype inherits `%IteratorPrototype%` (so it's tagged
52/// `[object Iterator]`) and `next` becomes enumerable. Idempotent — later
53/// calls skip the setup — so callers just build iterators and never register
54/// anything separately.
55pub fn live_iterator<'js, C>(ctx: &Ctx<'js>, iter: C) -> Result<Class<'js, C>>
56where
57    C: JsClass<'js> + 'js,
58{
59    let instance = Class::<C>::instance(ctx.clone(), iter)?;
60    if let Some(proto) = Class::<C>::prototype(ctx)? {
61        let iterator_proto = &BasePrimordials::get(ctx)?.prototype_iterator;
62        if proto.get_prototype().as_ref() != Some(iterator_proto) {
63            proto.set_prototype(Some(iterator_proto))?;
64            let next_fn: Function = proto.get(PredefinedAtom::Next)?;
65            proto.prop(
66                PredefinedAtom::Next,
67                Property::from(next_fn)
68                    .writable()
69                    .enumerable()
70                    .configurable(),
71            )?;
72        }
73    }
74    Ok(instance)
75}
76
77pub fn get_class_name(value: &Value) -> Result<Option<String>> {
78    value
79        .get_optional::<_, Object>(PredefinedAtom::Constructor)?
80        .and_then_ok(|ctor| ctor.get_optional::<_, String>(PredefinedAtom::Name))
81}
82
83#[inline(always)]
84pub fn get_class<'js, C>(provided: &Value<'js>) -> Result<Option<Class<'js, C>>>
85where
86    C: JsClass<'js>,
87{
88    if provided
89        .as_object()
90        .map(|p| p.instance_of::<C>())
91        .unwrap_or_default()
92    {
93        return Ok(Some(Class::<C>::from_value(provided)?));
94    }
95    Ok(None)
96}
97
98pub trait CustomInspectExtension<'js> {
99    fn define_with_custom_inspect(globals: &Object<'js>) -> Result<()>;
100}
101
102pub trait CustomInspect<'js>
103where
104    Self: JsClass<'js>,
105{
106    fn custom_inspect(&self, ctx: Ctx<'js>) -> Result<Object<'js>>;
107}
108
109impl<'js, C> CustomInspectExtension<'js> for Class<'js, C>
110where
111    C: JsClass<'js> + CustomInspect<'js> + 'js,
112{
113    fn define_with_custom_inspect(globals: &Object<'js>) -> Result<()> {
114        Self::define(globals)?;
115        let custom_inspect_symbol =
116            Symbol::new_global(globals.ctx().clone(), CUSTOM_INSPECT_SYMBOL_DESCRIPTION)?;
117        if let Some(proto) = Class::<C>::prototype(globals.ctx())? {
118            proto.prop(
119                custom_inspect_symbol,
120                Accessor::from(|this: This<Class<'js, C>>, ctx| this.borrow().custom_inspect(ctx)),
121            )?;
122        }
123        Ok(())
124    }
125}