Skip to main content

brioche_ducc/
object.rs

1use error::{Error, Result};
2use ffi;
3use std::marker::PhantomData;
4use types::Ref;
5use util::{protect_duktape_closure, StackGuard};
6use value::{FromValue, ToValue, ToValues, Value};
7
8/// Reference to a JavaScript object (guaranteed to not be an array or function).
9#[derive(Clone, Debug)]
10pub struct Object<'ducc>(pub(crate) Ref<'ducc>);
11
12impl<'ducc> Object<'ducc> {
13    /// Get an object property value using the given key. Returns `Value::Undefined` if no property
14    /// with the key exists.
15    ///
16    /// # Errors
17    ///
18    /// This function returns an error if:
19    ///
20    /// * `ToValue::to_value` fails for the key
21    /// * The `ToPropertyKey` implementation for the key fails
22    pub fn get<K: ToValue<'ducc>, V: FromValue<'ducc>>(&self, key: K) -> Result<V> {
23        let ducc = self.0.ducc;
24        let key = key.to_value(ducc)?;
25        let value = unsafe {
26            assert_stack!(ducc.ctx, 0, {
27                ducc.push_ref(&self.0);
28                ducc.push_value(key);
29                protect_duktape_closure(ducc.ctx, 2, 1, |ctx| {
30                    ffi::duk_get_prop(ctx, -2);
31                })?;
32                ducc.pop_value()
33            })
34        };
35        V::from_value(value, ducc)
36    }
37
38    /// Sets an object property using the given key and value.
39    ///
40    /// # Errors
41    ///
42    /// This function returns an error if:
43    ///
44    /// * `ToValue::to_value` fails for either the key or the value
45    /// * The `ToPropertyKey` implementation for the key fails
46    pub fn set<K: ToValue<'ducc>, V: ToValue<'ducc>>(&self, key: K, value: V) -> Result<()> {
47        let ducc = self.0.ducc;
48        let key = key.to_value(ducc)?;
49        let value = value.to_value(ducc)?;
50        unsafe {
51            assert_stack!(ducc.ctx, 0, {
52                ducc.push_ref(&self.0);
53                ducc.push_value(key);
54                ducc.push_value(value);
55                protect_duktape_closure(ducc.ctx, 3, 0, |ctx| {
56                    ffi::duk_put_prop(ctx, -3);
57                })
58            })
59        }
60    }
61
62    /// Removes the given key from the object. This function does nothing if the property does not
63    /// exist.
64    ///
65    /// # Errors
66    ///
67    /// This function returns an error if:
68    ///
69    /// * `ToValue::to_value` fails for the key
70    /// * The `ToPropertyKey` implementation for the key fails
71    pub fn remove<K: ToValue<'ducc>>(&self, key: K) -> Result<()> {
72        let ducc = self.0.ducc;
73        let key = key.to_value(ducc)?;
74        unsafe {
75            assert_stack!(ducc.ctx, 0, {
76                ducc.push_ref(&self.0);
77                ducc.push_value(key);
78                protect_duktape_closure(ducc.ctx, 2, 0, |ctx| {
79                    ffi::duk_del_prop(ctx, -2);
80                })
81            })
82        }
83    }
84
85    /// Returns `true` if the given key is a property of the object, `false` otherwise.
86    ///
87    /// # Errors
88    ///
89    /// This function returns an error if:
90    ///
91    /// * `ToValue::to_value` fails for the key
92    /// * The `ToPropertyKey` implementation for the key fails
93    pub fn contains_key<K: ToValue<'ducc>>(&self, key: K) -> Result<bool> {
94        let ducc = self.0.ducc;
95        let key = key.to_value(ducc)?;
96        unsafe {
97            assert_stack!(ducc.ctx, 0, {
98                ducc.push_ref(&self.0);
99                ducc.push_value(key);
100                protect_duktape_closure(ducc.ctx, 2, 0, |ctx| {
101                    ffi::duk_has_prop(ctx, -2) != 0
102                })
103            })
104        }
105    }
106
107    /// Returns the number of elements in the object using the calculation
108    /// `Math.floor(ToNumber(obj.length))`. This function can return an error if the `ToNumber`
109    /// implementation fails or if the `length` getter fails. Returns `Ok(0)` if the calculation
110    /// returns a number (a floating point in JavaScript land) outside of the range of `usize`.
111    pub fn len(&self) -> Result<usize> {
112        let ducc = self.0.ducc;
113        unsafe {
114            assert_stack!(ducc.ctx, 0, {
115                ducc.push_ref(&self.0);
116                protect_duktape_closure(ducc.ctx, 1, 0, |ctx| {
117                    ffi::duk_get_length(ctx, -1)
118                })
119            })
120        }
121    }
122
123    /// Calls the function at the key with the given arguments, with `this` set to the object.
124    /// Returns an error if the value at the key is not a function.
125    pub fn call_prop<K, A, R>(&self, key: K, args: A) -> Result<R>
126    where
127        K: ToValue<'ducc>,
128        A: ToValues<'ducc>,
129        R: FromValue<'ducc>,
130    {
131        let value: Value = self.get(key)?;
132        if let Some(func) = value.as_function() {
133            func.call_method(self.clone(), args)
134        } else {
135            Err(Error::not_a_function())
136        }
137    }
138
139    /// Returns an iterator over the object's keys and values, acting like a `for-in` loop: own and
140    /// inherited enumerable properties are included, and enumeration order follows the ES2015
141    /// `OwnPropertyKeys` enumeration order, applied for each inheritance level.
142    pub fn properties<K: FromValue<'ducc>, V: FromValue<'ducc>>(self) -> Properties<'ducc, K, V> {
143        let ducc = self.0.ducc;
144        unsafe {
145            let _sg = StackGuard::new(ducc.ctx);
146            ducc.push_ref(&self.0);
147            ffi::duk_require_stack(ducc.ctx, 1);
148            ffi::duk_enum(ducc.ctx, -1, 0);
149            Properties {
150                object_enum: ducc.pop_ref(),
151                _phantom: PhantomData,
152            }
153        }
154    }
155}
156
157pub struct Properties<'ducc, K, V> {
158    object_enum: Ref<'ducc>,
159    _phantom: PhantomData<(K, V)>,
160}
161
162impl<'ducc, K, V> Iterator for Properties<'ducc, K, V>
163where
164    K: FromValue<'ducc>,
165    V: FromValue<'ducc>,
166{
167    type Item = Result<(K, V)>;
168
169    fn next(&mut self) -> Option<Self::Item> {
170        let ducc = self.object_enum.ducc;
171        unsafe {
172            let _sg = StackGuard::new(ducc.ctx);
173            ducc.push_ref(&self.object_enum);
174            ffi::duk_require_stack(ducc.ctx, 2);
175            if ffi::duk_next(ducc.ctx, -1, 1) != 0 {
176                let value = match ducc.pop_value().into(ducc) {
177                    Ok(value) => value,
178                    Err(err) => return Some(Err(err)),
179                };
180                let key = match ducc.pop_value().into(ducc) {
181                    Ok(key) => key,
182                    Err(err) => return Some(Err(err)),
183                };
184                Some(Ok((key, value)))
185            } else {
186                None
187            }
188        }
189    }
190}