use super::ordered_set::SetLock;
use crate::{
builtins::{
iterable::create_iter_result_object, Array, BuiltInBuilder, IntrinsicObject, JsValue,
},
context::intrinsics::Intrinsics,
error::JsNativeError,
object::{JsObject, ObjectData},
property::{Attribute, PropertyNameKind},
realm::Realm,
symbol::JsSymbol,
Context, JsResult,
};
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
#[derive(Debug, Clone, Finalize, Trace)]
pub struct SetIterator {
iterated_set: JsValue,
next_index: usize,
#[unsafe_ignore_trace]
iteration_kind: PropertyNameKind,
lock: SetLock,
}
impl IntrinsicObject for SetIterator {
fn init(realm: &Realm) {
let _timer = Profiler::global().start_event("SetIterator", "init");
BuiltInBuilder::with_intrinsic::<Self>(realm)
.prototype(
realm
.intrinsics()
.objects()
.iterator_prototypes()
.iterator(),
)
.static_method(Self::next, "next", 0)
.static_property(
JsSymbol::to_string_tag(),
"Set Iterator",
Attribute::CONFIGURABLE,
)
.build();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.objects().iterator_prototypes().set()
}
}
impl SetIterator {
const fn new(set: JsValue, kind: PropertyNameKind, lock: SetLock) -> Self {
Self {
iterated_set: set,
next_index: 0,
iteration_kind: kind,
lock,
}
}
pub(crate) fn create_set_iterator(
set: JsValue,
kind: PropertyNameKind,
lock: SetLock,
context: &Context<'_>,
) -> JsValue {
let set_iterator = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
context.intrinsics().objects().iterator_prototypes().set(),
ObjectData::set_iterator(Self::new(set, kind, lock)),
);
set_iterator.into()
}
pub(crate) fn next(
this: &JsValue,
_: &[JsValue],
context: &mut Context<'_>,
) -> JsResult<JsValue> {
let mut set_iterator = this.as_object().map(JsObject::borrow_mut);
let set_iterator = set_iterator
.as_mut()
.and_then(|obj| obj.as_set_iterator_mut())
.ok_or_else(|| JsNativeError::typ().with_message("`this` is not an SetIterator"))?;
{
let m = &set_iterator.iterated_set;
let mut index = set_iterator.next_index;
let item_kind = &set_iterator.iteration_kind;
if set_iterator.iterated_set.is_undefined() {
return Ok(create_iter_result_object(
JsValue::undefined(),
true,
context,
));
}
let entries = m.as_object().map(JsObject::borrow);
let entries = entries
.as_ref()
.and_then(|obj| obj.as_set())
.ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?;
let num_entries = entries.full_len();
while index < num_entries {
let e = entries.get_index(index);
index += 1;
set_iterator.next_index = index;
if let Some(value) = e {
match item_kind {
PropertyNameKind::Value => {
return Ok(create_iter_result_object(value.clone(), false, context));
}
PropertyNameKind::KeyAndValue => {
let result = Array::create_array_from_list(
[value.clone(), value.clone()],
context,
);
return Ok(create_iter_result_object(result.into(), false, context));
}
PropertyNameKind::Key => {
panic!("tried to collect only keys of Set")
}
}
}
}
}
set_iterator.iterated_set = JsValue::undefined();
Ok(create_iter_result_object(
JsValue::undefined(),
true,
context,
))
}
}