Skip to main content

nanvm_lib/vm/object/
own_property.rs

1use super::Object;
2use crate::{
3    common::sized_index::SizedIndex,
4    vm::{Any, IVm, String},
5};
6
7impl<A: IVm> Object<A> {
8    /// `Object.getOwnPropertyDescriptor(self, key)?.value` — a flat lookup
9    /// by key, with no getter invocation and no prototype chain to walk.
10    /// `nanvm-lib` objects have no `__proto__`/prototype chain at all, so
11    /// this is simply what property lookup already is here — there is no
12    /// second, chain-walking accessor for this to differ from. `None` is
13    /// "no such own property", distinct from `Some` of a present
14    /// `undefined`, which `Any::has_entry` tells apart where `Any::entry`
15    /// answers `undefined` for both; a caller that wants the value itself
16    /// uses this.
17    ///
18    /// Searches from the *last* entry backward: an object's property list
19    /// is never deduplicated on construction (`fjs/edag/module.f.mjs`'s own
20    /// comment on `'{}'`: "duplicate keys are allowed with the later entry
21    /// winning"), so the last-written entry for a repeated key is the one
22    /// a lookup must answer with, not the first.
23    ///
24    /// ```
25    /// use nanvm_lib::{naive::Naive, vm::{Any, IVm, Object, ToObject}};
26    /// fn own_property_test<A: IVm>() {
27    ///     let o: Object<A> = [("a".into(), Any::undefined())].to_object();
28    ///     assert_eq!(o.own_property(&"a".into()), Some(Any::undefined()));
29    ///     assert_eq!(o.own_property(&"b".into()), None);
30    /// }
31    ///
32    /// own_property_test::<Naive>();
33    /// ```
34    pub fn own_property(&self, key: &String<A>) -> Option<Any<A>> {
35        (0..self.length())
36            .rev()
37            .map(|i| &self[i])
38            .find(|(k, _)| k == key)
39            .map(|(_, v)| v.clone())
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use crate::{
46        naive::Naive,
47        vm::{Object, String, ToAny, ToObject},
48    };
49
50    type T = Object<Naive>;
51
52    fn key(s: &str) -> String<Naive> {
53        s.into()
54    }
55
56    #[test]
57    fn finds_present_property() {
58        let obj: T = [(key("a"), 7.0.to_any())].to_object();
59        assert_eq!(obj.own_property(&key("a")), Some(7.0.to_any()));
60    }
61
62    #[test]
63    fn missing_property_is_none() {
64        let obj: T = [].to_object();
65        assert_eq!(obj.own_property(&key("a")), None);
66    }
67
68    #[test]
69    fn distinguishes_keys() {
70        let obj: T = [(key("a"), 1.0.to_any()), (key("b"), 2.0.to_any())].to_object();
71        assert_eq!(obj.own_property(&key("a")), Some(1.0.to_any()));
72        assert_eq!(obj.own_property(&key("b")), Some(2.0.to_any()));
73        assert_eq!(obj.own_property(&key("c")), None);
74    }
75
76    #[test]
77    fn empty_object_has_no_properties() {
78        let obj: T = [].to_object();
79        assert_eq!(obj.own_property(&key("toString")), None);
80    }
81
82    #[test]
83    fn later_duplicate_key_wins() {
84        // `{ a: 1, a: 2 }.a` is `2` in JS — an object's property list is
85        // never deduplicated on construction, so the lookup itself has to
86        // prefer the last entry rather than the first.
87        let obj: T = [(key("a"), 1.0.to_any()), (key("a"), 2.0.to_any())].to_object();
88        assert_eq!(obj.own_property(&key("a")), Some(2.0.to_any()));
89    }
90}