boa/builtins/object/
for_in_iterator.rs1use crate::{
2 builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
3 gc::{Finalize, Trace},
4 object::{JsObject, ObjectData},
5 property::PropertyDescriptor,
6 property::PropertyKey,
7 symbol::WellKnownSymbols,
8 BoaProfiler, Context, JsResult, JsString, JsValue,
9};
10use rustc_hash::FxHashSet;
11use std::collections::VecDeque;
12
13#[derive(Debug, Clone, Finalize, Trace)]
21pub struct ForInIterator {
22 object: JsValue,
23 visited_keys: FxHashSet<JsString>,
24 remaining_keys: VecDeque<JsString>,
25 object_was_visited: bool,
26}
27
28impl ForInIterator {
29 pub(crate) const NAME: &'static str = "ForInIterator";
30
31 fn new(object: JsValue) -> Self {
32 ForInIterator {
33 object,
34 visited_keys: FxHashSet::default(),
35 remaining_keys: VecDeque::default(),
36 object_was_visited: false,
37 }
38 }
39
40 pub(crate) fn create_for_in_iterator(object: JsValue, context: &Context) -> JsValue {
49 let for_in_iterator = JsValue::new_object(context);
50 for_in_iterator.set_data(ObjectData::for_in_iterator(Self::new(object)));
51 for_in_iterator
52 .as_object()
53 .expect("for in iterator object")
54 .set_prototype_instance(context.iterator_prototypes().for_in_iterator().into());
55 for_in_iterator
56 }
57
58 pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
67 if let JsValue::Object(ref o) = this {
68 let mut for_in_iterator = o.borrow_mut();
69 if let Some(iterator) = for_in_iterator.as_for_in_iterator_mut() {
70 let mut object = iterator.object.to_object(context)?;
71 loop {
72 if !iterator.object_was_visited {
73 let keys = object.__own_property_keys__(context)?;
74 for k in keys {
75 match k {
76 PropertyKey::String(ref k) => {
77 iterator.remaining_keys.push_back(k.clone());
78 }
79 PropertyKey::Index(i) => {
80 iterator.remaining_keys.push_back(i.to_string().into());
81 }
82 _ => {}
83 }
84 }
85 iterator.object_was_visited = true;
86 }
87 while let Some(r) = iterator.remaining_keys.pop_front() {
88 if !iterator.visited_keys.contains(&r) {
89 if let Some(desc) = object
90 .__get_own_property__(&PropertyKey::from(r.clone()), context)?
91 {
92 iterator.visited_keys.insert(r.clone());
93 if desc.expect_enumerable() {
94 return Ok(create_iter_result_object(
95 JsValue::new(r.to_string()),
96 false,
97 context,
98 ));
99 }
100 }
101 }
102 }
103 match object.prototype_instance().to_object(context) {
104 Ok(o) => {
105 object = o;
106 }
107 _ => {
108 return Ok(create_iter_result_object(
109 JsValue::undefined(),
110 true,
111 context,
112 ))
113 }
114 }
115 iterator.object = JsValue::new(object.clone());
116 iterator.object_was_visited = false;
117 }
118 } else {
119 context.throw_type_error("`this` is not a ForInIterator")
120 }
121 } else {
122 context.throw_type_error("`this` is not an ForInIterator")
123 }
124 }
125
126 pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
133 let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
134
135 let for_in_iterator = context.construct_object();
137 make_builtin_fn(Self::next, "next", &for_in_iterator, 0, context);
138 for_in_iterator.set_prototype_instance(iterator_prototype);
139
140 let to_string_tag = WellKnownSymbols::to_string_tag();
141 let to_string_tag_property = PropertyDescriptor::builder()
142 .value("For In Iterator")
143 .writable(false)
144 .enumerable(false)
145 .configurable(true);
146 for_in_iterator.insert(to_string_tag, to_string_tag_property);
147 for_in_iterator
148 }
149}