mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 12:47:35 +00:00
LibJS: Avoid creating String object wrappers in iterator helpers
This is a normative change in the Iterator Helpers spec. See:
3e275cf
This commit is contained in:
parent
acd8c94e88
commit
ff4e0d2943
5 changed files with 39 additions and 18 deletions
|
@ -39,11 +39,15 @@ ThrowCompletionOr<IteratorRecord> get_iterator_direct(VM& vm, Object& object)
|
||||||
return iterator_record;
|
return iterator_record;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowCompletionOr<IteratorRecord> get_iterator_flattenable(VM& vm, Value object)
|
// 2.1.2 GetIteratorFlattenable ( obj, stringHandling ), https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable
|
||||||
|
ThrowCompletionOr<IteratorRecord> get_iterator_flattenable(VM& vm, Value object, StringHandling string_handling)
|
||||||
{
|
{
|
||||||
// 1. If obj is not an Object, throw a TypeError exception.
|
// 1. If obj is not an Object, then
|
||||||
if (!object.is_object())
|
if (!object.is_object()) {
|
||||||
|
// a. If stringHandling is reject-strings or obj is not a String, throw a TypeError exception.
|
||||||
|
if (string_handling == StringHandling::RejectStrings || !object.is_string())
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, "obj"sv);
|
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, "obj"sv);
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Let method be ? GetMethod(obj, @@iterator).
|
// 2. Let method be ? GetMethod(obj, @@iterator).
|
||||||
auto method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
|
auto method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
|
||||||
|
|
|
@ -35,7 +35,12 @@ private:
|
||||||
IteratorRecord m_iterated; // [[Iterated]]
|
IteratorRecord m_iterated; // [[Iterated]]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class StringHandling {
|
||||||
|
IterateStrings,
|
||||||
|
RejectStrings,
|
||||||
|
};
|
||||||
|
|
||||||
ThrowCompletionOr<IteratorRecord> get_iterator_direct(VM&, Object&);
|
ThrowCompletionOr<IteratorRecord> get_iterator_direct(VM&, Object&);
|
||||||
ThrowCompletionOr<IteratorRecord> get_iterator_flattenable(VM&, Value);
|
ThrowCompletionOr<IteratorRecord> get_iterator_flattenable(VM&, Value, StringHandling);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,27 +66,23 @@ JS_DEFINE_NATIVE_FUNCTION(IteratorConstructor::from)
|
||||||
|
|
||||||
auto object = vm.argument(0);
|
auto object = vm.argument(0);
|
||||||
|
|
||||||
// 1. If O is a String, set O to ! ToObject(O).
|
// 1. Let iteratorRecord be ? GetIteratorFlattenable(O, iterate-strings).
|
||||||
if (object.is_string())
|
auto iterator_record = TRY(get_iterator_flattenable(vm, object, StringHandling::IterateStrings));
|
||||||
object = MUST_OR_THROW_OOM(object.to_object(vm));
|
|
||||||
|
|
||||||
// 2. Let iteratorRecord be ? GetIteratorFlattenable(O).
|
// 2. Let hasInstance be ? OrdinaryHasInstance(%Iterator%, iteratorRecord.[[Iterator]]).
|
||||||
auto iterator_record = TRY(get_iterator_flattenable(vm, object));
|
|
||||||
|
|
||||||
// 3. Let hasInstance be ? OrdinaryHasInstance(%Iterator%, iteratorRecord.[[Iterator]]).
|
|
||||||
auto has_instance = TRY(ordinary_has_instance(vm, iterator_record.iterator, realm.intrinsics().iterator_constructor()));
|
auto has_instance = TRY(ordinary_has_instance(vm, iterator_record.iterator, realm.intrinsics().iterator_constructor()));
|
||||||
|
|
||||||
// 4. If hasInstance is true, then
|
// 3. If hasInstance is true, then
|
||||||
if (has_instance.is_boolean() && has_instance.as_bool()) {
|
if (has_instance.is_boolean() && has_instance.as_bool()) {
|
||||||
// a. Return iteratorRecord.[[Iterator]].
|
// a. Return iteratorRecord.[[Iterator]].
|
||||||
return iterator_record.iterator;
|
return iterator_record.iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Let wrapper be OrdinaryObjectCreate(%WrapForValidIteratorPrototype%, « [[Iterated]] »).
|
// 4. Let wrapper be OrdinaryObjectCreate(%WrapForValidIteratorPrototype%, « [[Iterated]] »).
|
||||||
// 6. Set wrapper.[[Iterated]] to iteratorRecord.
|
// 5. Set wrapper.[[Iterated]] to iteratorRecord.
|
||||||
auto wrapper = MUST_OR_THROW_OOM(Iterator::create(realm, realm.intrinsics().wrap_for_valid_iterator_prototype(), move(iterator_record)));
|
auto wrapper = MUST_OR_THROW_OOM(Iterator::create(realm, realm.intrinsics().wrap_for_valid_iterator_prototype(), move(iterator_record)));
|
||||||
|
|
||||||
// 7. Return wrapper.
|
// 6. Return wrapper.
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -359,8 +359,8 @@ private:
|
||||||
if (mapped.is_error())
|
if (mapped.is_error())
|
||||||
return iterator.close_result(mapped.release_error());
|
return iterator.close_result(mapped.release_error());
|
||||||
|
|
||||||
// vi. Let innerIterator be Completion(GetIteratorFlattenable(mapped)).
|
// vi. Let innerIterator be Completion(GetIteratorFlattenable(mapped, reject-strings)).
|
||||||
auto inner_iterator = get_iterator_flattenable(vm, mapped.release_value());
|
auto inner_iterator = get_iterator_flattenable(vm, mapped.release_value(), StringHandling::RejectStrings);
|
||||||
|
|
||||||
// vii. IfAbruptCloseIterator(innerIterator, iterated).
|
// vii. IfAbruptCloseIterator(innerIterator, iterated).
|
||||||
if (inner_iterator.is_error())
|
if (inner_iterator.is_error())
|
||||||
|
|
|
@ -60,6 +60,22 @@ describe("normal behavior", () => {
|
||||||
expect(result.done).toBeTrue();
|
expect(result.done).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("does not coerce strings to objects", () => {
|
||||||
|
const stringIterator = String.prototype[Symbol.iterator];
|
||||||
|
let observedType = null;
|
||||||
|
|
||||||
|
Object.defineProperty(String.prototype, Symbol.iterator, {
|
||||||
|
get() {
|
||||||
|
"use strict";
|
||||||
|
observedType = typeof this;
|
||||||
|
return stringIterator;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Iterator.from("ab");
|
||||||
|
expect(observedType).toBe("string");
|
||||||
|
});
|
||||||
|
|
||||||
test("create Iterator from generator", () => {
|
test("create Iterator from generator", () => {
|
||||||
function* generator() {
|
function* generator() {
|
||||||
yield 1;
|
yield 1;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue