boa/builtins/map/
map_iterator.rs1use crate::{
2 builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue},
3 object::{JsObject, ObjectData},
4 property::{PropertyDescriptor, PropertyNameKind},
5 symbol::WellKnownSymbols,
6 BoaProfiler, Context, JsResult,
7};
8use gc::{Finalize, Trace};
9
10use super::ordered_map::MapLock;
11#[derive(Debug, Clone, Finalize, Trace)]
18pub struct MapIterator {
19 iterated_map: Option<JsObject>,
20 map_next_index: usize,
21 map_iteration_kind: PropertyNameKind,
22 lock: MapLock,
23}
24
25impl MapIterator {
26 pub(crate) const NAME: &'static str = "MapIterator";
27
28 pub(crate) fn create_map_iterator(
37 map: &JsValue,
38 kind: PropertyNameKind,
39 context: &mut Context,
40 ) -> JsResult<JsValue> {
41 if let Some(map_obj) = map.as_object() {
42 if let Some(map) = map_obj.borrow_mut().as_map_mut() {
43 let lock = map.lock(map_obj.clone());
44 let iter = MapIterator {
45 iterated_map: Some(map_obj.clone()),
46 map_next_index: 0,
47 map_iteration_kind: kind,
48 lock,
49 };
50 let map_iterator = JsValue::new_object(context);
51 map_iterator.set_data(ObjectData::map_iterator(iter));
52 map_iterator
53 .as_object()
54 .expect("map iterator object")
55 .set_prototype_instance(context.iterator_prototypes().map_iterator().into());
56 return Ok(map_iterator);
57 }
58 }
59 context.throw_type_error("`this` is not a Map")
60 }
61
62 pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
71 let iterator_object = match this {
72 JsValue::Object(obj) if obj.borrow().is_map_iterator() => obj,
73 _ => return context.throw_type_error("`this` is not a MapIterator"),
74 };
75
76 let mut iterator_object = iterator_object.borrow_mut();
77
78 let map_iterator = iterator_object
79 .as_map_iterator_mut()
80 .expect("checked that obj was a map iterator");
81
82 let mut index = map_iterator.map_next_index;
83 let item_kind = map_iterator.map_iteration_kind;
84
85 if let Some(obj) = map_iterator.iterated_map.take() {
86 let map = obj.borrow();
87 let entries = map.as_map_ref().expect("iterator should only iterate maps");
88 let num_entries = entries.full_len();
89 while index < num_entries {
90 let e = entries.get_index(index);
91 index += 1;
92 map_iterator.map_next_index = index;
93 if let Some((key, value)) = e {
94 let item = match item_kind {
95 PropertyNameKind::Key => {
96 Ok(create_iter_result_object(key.clone(), false, context))
97 }
98 PropertyNameKind::Value => {
99 Ok(create_iter_result_object(value.clone(), false, context))
100 }
101 PropertyNameKind::KeyAndValue => {
102 let result = Array::create_array_from_list(
103 [key.clone(), value.clone()],
104 context,
105 );
106 Ok(create_iter_result_object(result.into(), false, context))
107 }
108 };
109 drop(map);
110 map_iterator.iterated_map = Some(obj);
111 return item;
112 }
113 }
114 }
115
116 Ok(create_iter_result_object(
117 JsValue::undefined(),
118 true,
119 context,
120 ))
121 }
122
123 pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
130 let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
131
132 let map_iterator = context.construct_object();
134 make_builtin_fn(Self::next, "next", &map_iterator, 0, context);
135 map_iterator.set_prototype_instance(iterator_prototype);
136
137 let to_string_tag = WellKnownSymbols::to_string_tag();
138 let to_string_tag_property = PropertyDescriptor::builder()
139 .value("Map Iterator")
140 .writable(false)
141 .enumerable(false)
142 .configurable(true);
143 map_iterator.insert(to_string_tag, to_string_tag_property);
144 map_iterator
145 }
146}