use std::collections::VecDeque;
use crate::{
Context, JsArgs, JsData, JsResult, JsString, JsValue,
builtins::{
BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,
iterable::iterator_helper::{self, IterableRecord},
object::OrdinaryObject,
},
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
error::JsNativeError,
js_error, js_string,
object::{JsFunction, JsObject, PROTOTYPE, internal_methods::get_prototype_from_constructor},
property::Attribute,
realm::Realm,
string::StaticJsStrings,
symbol::JsSymbol,
};
use boa_gc::{Finalize, Trace};
use super::{iterator_helper::IteratorHelper, wrap_for_valid_iterator::WrapForValidIterator};
#[derive(Debug, Clone, Trace, Finalize, JsData)]
pub(crate) struct IteratorConstructor;
impl IntrinsicObject for IteratorConstructor {
fn init(realm: &Realm) {
let iterator_prototype = realm.intrinsics().constructors().iterator().prototype();
BuiltInBuilder::from_standard_constructor::<Self>(realm)
.inherits(Some(iterator_prototype.clone()))
.static_method(Self::from, js_string!("from"), 1)
.static_method(Self::concat, js_string!("concat"), 0)
.static_property(PROTOTYPE, iterator_prototype, Attribute::empty())
.build_without_prototype();
}
fn get(intrinsics: &Intrinsics) -> JsObject {
intrinsics.constructors().iterator().constructor()
}
}
impl BuiltInObject for IteratorConstructor {
const NAME: JsString = StaticJsStrings::ITERATOR;
}
impl BuiltInConstructor for IteratorConstructor {
const PROTOTYPE_STORAGE_SLOTS: usize = 0;
const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;
const CONSTRUCTOR_ARGUMENTS: usize = 0;
const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =
StandardConstructors::iterator;
fn constructor(
new_target: &JsValue,
_args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
if new_target.is_undefined()
|| new_target
== &context
.active_function_object()
.unwrap_or_else(|| context.intrinsics().constructors().iterator().constructor())
.into()
{
return Err(JsNativeError::typ()
.with_message(if new_target.is_undefined() {
"Iterator constructor requires 'new'"
} else {
"Abstract class Iterator not directly constructable"
})
.into());
}
let prototype =
get_prototype_from_constructor(new_target, StandardConstructors::iterator, context)?;
Ok(JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
OrdinaryObject,
)
.upcast()
.into())
}
}
impl IteratorConstructor {
fn from(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let o = args.get_or_undefined(0);
let iterator_record = super::get_iterator_flattenable(o, true, context)?;
let iterator_constructor = context.intrinsics().constructors().iterator().constructor();
let has_instance = JsValue::ordinary_has_instance(
&iterator_constructor.clone().into(),
&iterator_record.iterator().clone().into(),
context,
)?;
if has_instance {
return Ok(iterator_record.iterator().clone().into());
}
let wrapper = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
context
.intrinsics()
.objects()
.iterator_prototypes()
.wrap_for_valid_iterator(),
WrapForValidIterator {
iterated: iterator_record,
},
);
Ok(wrapper.into())
}
fn concat(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let mut iterables = VecDeque::with_capacity(args.len());
for item in args {
let Some(item) = item.as_object() else {
return Err(js_error!(TypeError: "Iterator.concat requires iterable objects"));
};
let method = item.get_method(JsSymbol::iterator(), context)?.ok_or_else(
|| js_error!(TypeError: "Iterator.concat requires objects with @@iterator"),
)?;
iterables.push_back(IterableRecord {
iterable: item,
open_method: JsFunction::from_object_unchecked(method),
});
}
let helper = IteratorHelper::create(iterator_helper::Concat::new(iterables), context);
Ok(helper.into())
}
}