diff --git a/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp b/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp index c74fbbd4f6f..9016dc4075c 100644 --- a/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp @@ -306,6 +306,8 @@ JS_ENUMERATE_TYPED_ARRAYS initialize_constructor(vm, vm.names.Boolean, *m_##snake_namespace##snake_name##_constructor, m_##snake_namespace##snake_name##_prototype); \ else if constexpr (IsSame) \ initialize_constructor(vm, vm.names.Function, *m_##snake_namespace##snake_name##_constructor, m_##snake_namespace##snake_name##_prototype); \ + else if constexpr (IsSame) \ + initialize_constructor(vm, vm.names.Iterator, *m_##snake_namespace##snake_name##_constructor, nullptr); \ else if constexpr (IsSame) \ initialize_constructor(vm, vm.names.Number, *m_##snake_namespace##snake_name##_constructor, m_##snake_namespace##snake_name##_prototype); \ else if constexpr (IsSame) \ diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp index 071b9070027..0f2d5ffe9f7 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,9 @@ void IteratorPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.every, every, 1, attr); define_native_function(realm, vm.names.find, find, 1, attr); + // 3.1.3.1 Iterator.prototype.constructor, https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.constructor + define_native_accessor(realm, vm.names.constructor, constructor_getter, constructor_setter, Attribute::Configurable); + // 3.1.3.13 Iterator.prototype [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype-@@tostringtag define_native_accessor(realm, vm.well_known_symbol_to_string_tag(), to_string_tag_getter, to_string_tag_setter, Attribute::Configurable); } @@ -54,6 +58,27 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::symbol_iterator) return vm.this_value(); } +// 3.1.3.1.1 get Iterator.prototype.constructor, https://tc39.es/proposal-iterator-helpers/#sec-get-iteratorprototype-constructor +JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::constructor_getter) +{ + auto& realm = *vm.current_realm(); + + // 1. Return %Iterator%. + return realm.intrinsics().iterator_constructor(); +} + +// 3.1.3.1.2 set Iterator.prototype.constructor, https://tc39.es/proposal-iterator-helpers/#sec-set-iteratorprototype-constructor +JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::constructor_setter) +{ + auto& realm = *vm.current_realm(); + + // 1. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Iterator.prototype%, "constructor", v). + TRY(setter_that_ignores_prototype_properties(vm, vm.this_value(), realm.intrinsics().iterator_prototype(), vm.names.constructor, vm.argument(0))); + + // 2. Return undefined. + return js_undefined(); +} + // 3.1.3.2 Iterator.prototype.map ( mapper ), https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype.map JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::map) { diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h index b0db25d08c4..5382092e483 100644 --- a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h @@ -22,6 +22,9 @@ public: private: IteratorPrototype(Realm&); + JS_DECLARE_NATIVE_FUNCTION(constructor_getter); + JS_DECLARE_NATIVE_FUNCTION(constructor_setter); + JS_DECLARE_NATIVE_FUNCTION(symbol_iterator); JS_DECLARE_NATIVE_FUNCTION(map); JS_DECLARE_NATIVE_FUNCTION(filter); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Iterator/Iterator.prototype.constructor.js b/Userland/Libraries/LibJS/Tests/builtins/Iterator/Iterator.prototype.constructor.js new file mode 100644 index 00000000000..af8cde857a8 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Iterator/Iterator.prototype.constructor.js @@ -0,0 +1,33 @@ +const sentinel = "whf :^)"; + +describe("errors", () => { + test("setter called on non-object", () => { + let { get, set } = Object.getOwnPropertyDescriptor(Iterator.prototype, "constructor"); + + expect(() => { + set.call(undefined, sentinel); + }).toThrowWithMessage(TypeError, "undefined is not an object"); + }); + + test("cannot set the built-in Iterator's constructor", () => { + expect(() => { + Iterator.prototype.constructor = sentinel; + }).toThrowWithMessage( + TypeError, + "Cannot write to non-writable property '[object IteratorPrototype]'" + ); + }); +}); + +describe("correct behavior", () => { + test("basic functionality", () => { + expect(Iterator.prototype.constructor).toBe(Iterator); + }); + + test("constructor setter", () => { + let Proto = Object.create(Iterator.prototype); + Proto.constructor = sentinel; + + expect(Proto.constructor).toBe(sentinel); + }); +});