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}