boa/builtins/string/
string_iterator.rs1use crate::{
2 builtins::{
3 function::make_builtin_fn, iterable::create_iter_result_object, string::code_point_at,
4 },
5 gc::{Finalize, Trace},
6 object::{JsObject, ObjectData},
7 property::PropertyDescriptor,
8 symbol::WellKnownSymbols,
9 BoaProfiler, Context, JsResult, JsValue,
10};
11
12#[derive(Debug, Clone, Finalize, Trace)]
13pub struct StringIterator {
14 string: JsValue,
15 next_index: i32,
16}
17
18impl StringIterator {
19 fn new(string: JsValue) -> Self {
20 Self {
21 string,
22 next_index: 0,
23 }
24 }
25
26 pub fn create_string_iterator(string: JsValue, context: &mut Context) -> JsResult<JsValue> {
27 let string_iterator = JsValue::new_object(context);
28 string_iterator.set_data(ObjectData::string_iterator(Self::new(string)));
29 string_iterator
30 .as_object()
31 .expect("array iterator object")
32 .set_prototype_instance(context.iterator_prototypes().string_iterator().into());
33 Ok(string_iterator)
34 }
35
36 pub fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
37 if let JsValue::Object(ref object) = this {
38 let mut object = object.borrow_mut();
39 if let Some(string_iterator) = object.as_string_iterator_mut() {
40 if string_iterator.string.is_undefined() {
41 return Ok(create_iter_result_object(
42 JsValue::undefined(),
43 true,
44 context,
45 ));
46 }
47 let native_string = string_iterator.string.to_string(context)?;
48 let len = native_string.encode_utf16().count() as i32;
49 let position = string_iterator.next_index;
50 if position >= len {
51 string_iterator.string = JsValue::undefined();
52 return Ok(create_iter_result_object(
53 JsValue::undefined(),
54 true,
55 context,
56 ));
57 }
58 let (_, code_unit_count, _) =
59 code_point_at(native_string, position).expect("Invalid code point position");
60 string_iterator.next_index += code_unit_count as i32;
61 let result_string = crate::builtins::string::String::substring(
62 &string_iterator.string,
63 &[position.into(), string_iterator.next_index.into()],
64 context,
65 )?;
66 Ok(create_iter_result_object(result_string, false, context))
67 } else {
68 context.throw_type_error("`this` is not an ArrayIterator")
69 }
70 } else {
71 context.throw_type_error("`this` is not an ArrayIterator")
72 }
73 }
74
75 pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
82 let _timer = BoaProfiler::global().start_event("String Iterator", "init");
83
84 let array_iterator = context.construct_object();
86 make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
87 array_iterator.set_prototype_instance(iterator_prototype);
88
89 let to_string_tag = WellKnownSymbols::to_string_tag();
90 let to_string_tag_property = PropertyDescriptor::builder()
91 .value("String Iterator")
92 .writable(false)
93 .enumerable(false)
94 .configurable(true);
95 array_iterator.insert(to_string_tag, to_string_tag_property);
96 array_iterator
97 }
98}