mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-25 10:48:53 +00:00
LibJS: Implement Iterator.prototype [ @@toStringTag ] according to spec
The spec allows setting the prototype on non built-in Iterator objects.
This is a normative change in the Iterator Helpers proposal. See:
30b3501
This commit is contained in:
parent
2225b837f8
commit
734e37442d
Notes:
sideshowbarker
2024-07-18 23:45:47 +09:00
Author: https://github.com/trflynn89
Commit: 734e37442d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/663
5 changed files with 94 additions and 5 deletions
|
@ -353,6 +353,42 @@ ThrowCompletionOr<MarkedVector<Value>> iterator_to_list(VM& vm, IteratorRecord&
|
|||
}
|
||||
}
|
||||
|
||||
// 2.1 SetterThatIgnoresPrototypeProperties ( this, home, p, v ), https://tc39.es/proposal-iterator-helpers/#sec-SetterThatIgnoresPrototypeProperties
|
||||
ThrowCompletionOr<void> setter_that_ignores_prototype_properties(VM& vm, Value this_, Object const& home, PropertyKey const& property, Value value)
|
||||
{
|
||||
// 1. If this is not an Object, then
|
||||
if (!this_.is_object()) {
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, this_);
|
||||
}
|
||||
|
||||
auto& this_object = this_.as_object();
|
||||
|
||||
// 2. If this is home, then
|
||||
if (&this_object == &home) {
|
||||
// a. NOTE: Throwing here emulates assignment to a non-writable data property on the home object in strict mode code.
|
||||
// b. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::DescWriteNonWritable, this_);
|
||||
}
|
||||
|
||||
// 3. Let desc be ? this.[[GetOwnProperty]](p).
|
||||
auto desc = TRY(this_object.internal_get_own_property(property));
|
||||
|
||||
// 4. If desc is undefined, then
|
||||
if (!desc.has_value()) {
|
||||
// a. Perform ? CreateDataPropertyOrThrow(this, p, v).
|
||||
TRY(this_object.create_data_property_or_throw(property, value));
|
||||
}
|
||||
// 5. Else,
|
||||
else {
|
||||
// a. Perform ? Set(this, p, v, true).
|
||||
TRY(this_object.set(property, value, Object::ShouldThrowExceptions::Yes));
|
||||
}
|
||||
|
||||
// 6. Return unused.
|
||||
return {};
|
||||
}
|
||||
|
||||
// Non-standard
|
||||
Completion get_iterator_values(VM& vm, Value iterable, IteratorValueCallback callback)
|
||||
{
|
||||
|
|
|
@ -83,6 +83,7 @@ Completion iterator_close(VM&, IteratorRecord const&, Completion);
|
|||
Completion async_iterator_close(VM&, IteratorRecord const&, Completion);
|
||||
NonnullGCPtr<Object> create_iterator_result_object(VM&, Value, bool done);
|
||||
ThrowCompletionOr<MarkedVector<Value>> iterator_to_list(VM&, IteratorRecord&);
|
||||
ThrowCompletionOr<void> setter_that_ignores_prototype_properties(VM&, Value this_, Object const& home, PropertyKey const& property, Value value);
|
||||
|
||||
using IteratorValueCallback = Function<Optional<Completion>(Value)>;
|
||||
Completion get_iterator_values(VM&, Value iterable, IteratorValueCallback callback);
|
||||
|
|
|
@ -29,9 +29,6 @@ void IteratorPrototype::initialize(Realm& realm)
|
|||
auto& vm = this->vm();
|
||||
Base::initialize(realm);
|
||||
|
||||
// 3.1.3.13 Iterator.prototype [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-iteratorprototype-@@tostringtag
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Iterator"_string), Attribute::Configurable | Attribute::Writable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.well_known_symbol_iterator(), symbol_iterator, 0, attr);
|
||||
define_native_function(realm, vm.names.map, map, 1, attr);
|
||||
|
@ -45,6 +42,9 @@ void IteratorPrototype::initialize(Realm& realm)
|
|||
define_native_function(realm, vm.names.some, some, 1, attr);
|
||||
define_native_function(realm, vm.names.every, every, 1, attr);
|
||||
define_native_function(realm, vm.names.find, find, 1, attr);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 27.1.2.1 %IteratorPrototype% [ @@iterator ] ( ), https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator
|
||||
|
@ -749,4 +749,23 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::find)
|
|||
}
|
||||
}
|
||||
|
||||
// 3.1.3.13.1 get Iterator.prototype [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-get-iteratorprototype-@@tostringtag
|
||||
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::to_string_tag_getter)
|
||||
{
|
||||
// 1. Return "Iterator".
|
||||
return PrimitiveString::create(vm, vm.names.Iterator.as_string());
|
||||
}
|
||||
|
||||
// 3.1.3.13.2 set Iterator.prototype [ @@toStringTag ], https://tc39.es/proposal-iterator-helpers/#sec-set-iteratorprototype-@@tostringtag
|
||||
JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::to_string_tag_setter)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Perform ? SetterThatIgnoresPrototypeProperties(this value, %Iterator.prototype%, %Symbol.toStringTag%, v).
|
||||
TRY(setter_that_ignores_prototype_properties(vm, vm.this_value(), realm.intrinsics().iterator_prototype(), vm.well_known_symbol_to_string_tag(), vm.argument(0)));
|
||||
|
||||
// 2. Return undefined.
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,6 +34,9 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(some);
|
||||
JS_DECLARE_NATIVE_FUNCTION(every);
|
||||
JS_DECLARE_NATIVE_FUNCTION(find);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string_tag_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string_tag_setter);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,33 @@
|
|||
test("basic functionality", () => {
|
||||
expect(Iterator.prototype[Symbol.toStringTag]).toBe("Iterator");
|
||||
const sentinel = "whf :^)";
|
||||
|
||||
describe("errors", () => {
|
||||
test("setter called on non-object", () => {
|
||||
let { get, set } = Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag);
|
||||
|
||||
expect(() => {
|
||||
set.call(undefined, sentinel);
|
||||
}).toThrowWithMessage(TypeError, "undefined is not an object");
|
||||
});
|
||||
|
||||
test("cannot set the built-in Iterator's toStringTag", () => {
|
||||
expect(() => {
|
||||
Iterator.prototype[Symbol.toStringTag] = sentinel;
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
"Cannot write to non-writable property '[object IteratorPrototype]'"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
expect(Iterator.prototype[Symbol.toStringTag]).toBe("Iterator");
|
||||
});
|
||||
|
||||
test("toStringTag setter", () => {
|
||||
let Proto = Object.create(Iterator.prototype);
|
||||
Proto[Symbol.toStringTag] = sentinel;
|
||||
|
||||
expect(Proto[Symbol.toStringTag]).toBe(sentinel);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue