LibJS: Implement Iterator.prototype.constructor according to spec

The spec allows setting a constructor on non built-in Iterator objects.

This is a normative change in the Iterator Helpers proposal. See:
30b3501
This commit is contained in:
Timothy Flynn 2024-07-16 07:29:27 -04:00 committed by Andreas Kling
commit fb228a3d85
Notes: sideshowbarker 2024-07-18 23:45:44 +09:00
4 changed files with 63 additions and 0 deletions

View file

@ -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<Namespace::ConstructorName, FunctionConstructor>) \
initialize_constructor(vm, vm.names.Function, *m_##snake_namespace##snake_name##_constructor, m_##snake_namespace##snake_name##_prototype); \
else if constexpr (IsSame<Namespace::ConstructorName, IteratorConstructor>) \
initialize_constructor(vm, vm.names.Iterator, *m_##snake_namespace##snake_name##_constructor, nullptr); \
else if constexpr (IsSame<Namespace::ConstructorName, NumberConstructor>) \
initialize_constructor(vm, vm.names.Number, *m_##snake_namespace##snake_name##_constructor, m_##snake_namespace##snake_name##_prototype); \
else if constexpr (IsSame<Namespace::ConstructorName, RegExpConstructor>) \

View file

@ -10,6 +10,7 @@
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/IteratorConstructor.h>
#include <LibJS/Runtime/IteratorHelper.h>
#include <LibJS/Runtime/IteratorPrototype.h>
#include <LibJS/Runtime/ValueInlines.h>
@ -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)
{

View file

@ -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);

View file

@ -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);
});
});