diff --git a/boa_builtins/build.rs b/boa_builtins/build.rs index 71689e1e9c..93b713b823 100644 --- a/boa_builtins/build.rs +++ b/boa_builtins/build.rs @@ -620,6 +620,53 @@ fn main() -> io::Result<()> { .static_method(utf16!("of")) .build(file)?; + BuiltInBuilder::new(&context, "ARRAY_UNSCOPABLES_OBJECT") + .property( + utf16!("at"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("copyWithin"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("entries"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("fill"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("find"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("findIndex"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("flat"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("flatMap"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("includes"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("keys"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .property( + utf16!("values"), + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ) + .build(file)?; + BuiltInBuilderConstructor::new(&context, "DATE") .static_method(utf16!("now")) .static_method(utf16!("parse")) diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index bbb6d138d0..00d7a40f3f 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -55,6 +55,47 @@ impl BuiltInObject for ArrayPrototypeValues { const NAME: &'static str = "values"; } +pub(crate) struct ArrayPrototypeUnscopables; + +impl IntrinsicObject for ArrayPrototypeUnscopables { + fn init(realm: &Realm) { + BuiltInBuilder::with_intrinsic::( + realm, + &boa_builtins::ARRAY_UNSCOPABLES_OBJECT_STATIC_SHAPE, + ) + // 1. Let unscopableList be OrdinaryObjectCreate(null). + .no_prototype() + // 2. Perform ! CreateDataPropertyOrThrow(unscopableList, "at", true). + .static_property(true) + // 3. Perform ! CreateDataPropertyOrThrow(unscopableList, "copyWithin", true). + .static_property(true) + // 4. Perform ! CreateDataPropertyOrThrow(unscopableList, "entries", true). + .static_property(true) + // 5. Perform ! CreateDataPropertyOrThrow(unscopableList, "fill", true). + .static_property(true) + // 6. Perform ! CreateDataPropertyOrThrow(unscopableList, "find", true). + .static_property(true) + // 7. Perform ! CreateDataPropertyOrThrow(unscopableList, "findIndex", true). + .static_property(true) + // 8. Perform ! CreateDataPropertyOrThrow(unscopableList, "flat", true). + .static_property(true) + // 9. Perform ! CreateDataPropertyOrThrow(unscopableList, "flatMap", true). + .static_property(true) + // 10. Perform ! CreateDataPropertyOrThrow(unscopableList, "includes", true). + .static_property(true) + // 11. Perform ! CreateDataPropertyOrThrow(unscopableList, "keys", true). + .static_property(true) + // 12. Perform ! CreateDataPropertyOrThrow(unscopableList, "values", true). + .static_property(true) + // 13. Return unscopableList. + .build(); + } + + fn get(intrinsics: &Intrinsics) -> JsObject { + intrinsics.objects().array_prototype_unscopables() + } +} + /// JavaScript `Array` built-in implementation. #[derive(Debug, Clone, Copy)] pub(crate) struct Array; @@ -71,7 +112,7 @@ impl IntrinsicObject for Array { let values_function = realm.intrinsics().objects().array_prototype_values(); - let unscopables_object = Self::unscopables_object(); + let unscopables_object = Self::unscopables_object(realm); BuiltInBuilder::from_standard_constructor_static_shape::( realm, @@ -3031,41 +3072,8 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype-@@unscopables /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@unscopables - pub(crate) fn unscopables_object() -> JsObject { - // 1. Let unscopableList be OrdinaryObjectCreate(null). - let unscopable_list = JsObject::with_null_proto(); - let true_prop = PropertyDescriptor::builder() - .value(true) - .writable(true) - .enumerable(true) - .configurable(true); - { - let mut obj = unscopable_list.borrow_mut(); - // 2. Perform ! CreateDataPropertyOrThrow(unscopableList, "at", true). - obj.insert(utf16!("at"), true_prop.clone()); - // 3. Perform ! CreateDataPropertyOrThrow(unscopableList, "copyWithin", true). - obj.insert(utf16!("copyWithin"), true_prop.clone()); - // 4. Perform ! CreateDataPropertyOrThrow(unscopableList, "entries", true). - obj.insert(utf16!("entries"), true_prop.clone()); - // 5. Perform ! CreateDataPropertyOrThrow(unscopableList, "fill", true). - obj.insert(utf16!("fill"), true_prop.clone()); - // 6. Perform ! CreateDataPropertyOrThrow(unscopableList, "find", true). - obj.insert(utf16!("find"), true_prop.clone()); - // 7. Perform ! CreateDataPropertyOrThrow(unscopableList, "findIndex", true). - obj.insert(utf16!("findIndex"), true_prop.clone()); - // 8. Perform ! CreateDataPropertyOrThrow(unscopableList, "flat", true). - obj.insert(utf16!("flat"), true_prop.clone()); - // 9. Perform ! CreateDataPropertyOrThrow(unscopableList, "flatMap", true). - obj.insert(utf16!("flatMap"), true_prop.clone()); - // 10. Perform ! CreateDataPropertyOrThrow(unscopableList, "includes", true). - obj.insert(utf16!("includes"), true_prop.clone()); - // 11. Perform ! CreateDataPropertyOrThrow(unscopableList, "keys", true). - obj.insert(utf16!("keys"), true_prop.clone()); - // 12. Perform ! CreateDataPropertyOrThrow(unscopableList, "values", true). - obj.insert(utf16!("values"), true_prop); - } - - // 13. Return unscopableList. - unscopable_list + fn unscopables_object(realm: &Realm) -> JsObject { + ArrayPrototypeUnscopables::init(realm); + realm.intrinsics().objects().array_prototype_unscopables() } } diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 098617f376..07e91ac2d3 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -895,12 +895,17 @@ struct BuiltInBuilderStaticShape<'ctx> { object: JsObject, property_index: usize, storage: Vec, - prototype: JsObject, + prototype: Option, } impl BuiltInBuilderStaticShape<'_> { fn prototype(mut self, prototype: JsObject) -> Self { - self.prototype = prototype; + self.prototype = Some(prototype); + self + } + + fn no_prototype(mut self) -> Self { + self.prototype = None; self } @@ -953,7 +958,8 @@ impl BuiltInBuilderStaticShape<'_> { let mut object = self.object.borrow_mut(); object.properties_mut().shape = StaticShape::new(self.shape).into(); - self.storage.push(self.prototype.into()); + self.storage + .push(self.prototype.map_or(JsValue::undefined(), Into::into)); object.properties_mut().storage = self.storage; } } @@ -969,7 +975,7 @@ impl<'ctx> BuiltInBuilder { object: I::get(realm.intrinsics()), storage: Vec::with_capacity(shape.storage_len), property_index: 0, - prototype: realm.intrinsics().constructors().object().prototype(), + prototype: Some(realm.intrinsics().constructors().object().prototype()), } } } diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 23011e82ae..d1f6f75c36 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -776,6 +776,9 @@ pub struct IntrinsicObjects { /// [`%Array.prototype.values%`](https://tc39.es/ecma262/#sec-array.prototype.values) array_prototype_values: JsFunction, + /// [`Array.prototype[ @@unscopables ]`](https://tc39.es/ecma262/#sec-array.prototype-@@unscopables) + array_prototype_unscopables: JsObject, + /// Cached iterator prototypes. iterator_prototypes: IteratorPrototypes, @@ -828,6 +831,7 @@ impl Default for IntrinsicObjects { json: JsObject::default_with_static_shape(), throw_type_error: JsFunction::empty_intrinsic_function(false), array_prototype_values: JsFunction::empty_intrinsic_function_static_shape(false), + array_prototype_unscopables: JsObject::default_with_static_shape(), iterator_prototypes: IteratorPrototypes::default(), generator: JsObject::default_with_static_shape(), async_generator: JsObject::default_with_static_shape(), @@ -866,6 +870,14 @@ impl IntrinsicObjects { self.array_prototype_values.clone() } + /// Gets the [`Array.prototype[ @@unscopables ]`][spec] object. + /// + /// [spec]: https://tc39.es/ecma262/#sec-array.prototype-@@unscopables + #[inline] + pub(crate) fn array_prototype_unscopables(&self) -> JsObject { + self.array_prototype_unscopables.clone() + } + /// Gets the cached iterator prototypes. #[inline] pub const fn iterator_prototypes(&self) -> &IteratorPrototypes {