mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-11 17:00:37 +00:00
LibJS: Make Array.prototype.concat generic
This commit is contained in:
parent
2ef9df989f
commit
4152409ac5
Notes:
sideshowbarker
2024-07-18 12:16:29 +09:00
Author: https://github.com/davidot Commit: https://github.com/SerenityOS/serenity/commit/4152409ac5d Pull-request: https://github.com/SerenityOS/serenity/pull/8025 Reviewed-by: https://github.com/linusg
2 changed files with 109 additions and 10 deletions
|
@ -435,27 +435,87 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::join)
|
|||
// 23.1.3.1 Array.prototype.concat ( ...items ), https://tc39.es/ecma262/#sec-array.prototype.concat
|
||||
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::concat)
|
||||
{
|
||||
auto* array = Array::typed_this(vm, global_object);
|
||||
if (!array)
|
||||
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||
if (!this_object)
|
||||
return {};
|
||||
|
||||
auto* new_array = Array::create(global_object);
|
||||
new_array->indexed_properties().append_all(array, array->indexed_properties());
|
||||
// FIXME: Use ArraySpeciesCreate.
|
||||
auto new_array = Array::create(global_object);
|
||||
|
||||
size_t n = 0;
|
||||
|
||||
// 23.1.3.1.1 IsConcatSpreadable ( O ), https://tc39.es/ecma262/#sec-isconcatspreadable
|
||||
auto is_concat_spreadable = [&vm, &global_object](Value const& val) {
|
||||
if (!val.is_object()) {
|
||||
return false;
|
||||
}
|
||||
auto* object = val.to_object(global_object);
|
||||
if (vm.exception())
|
||||
return false;
|
||||
|
||||
auto spreadable = object->get(vm.well_known_symbol_is_concat_spreadable()).value_or(js_undefined());
|
||||
if (vm.exception())
|
||||
return false;
|
||||
|
||||
if (!spreadable.is_undefined())
|
||||
return spreadable.to_boolean();
|
||||
|
||||
return object->is_array();
|
||||
};
|
||||
|
||||
auto append_to_new_array = [&vm, &is_concat_spreadable, &new_array, &global_object, &n](Value arg) {
|
||||
auto spreadable = is_concat_spreadable(arg);
|
||||
if (vm.exception())
|
||||
return;
|
||||
if (spreadable) {
|
||||
VERIFY(arg.is_object());
|
||||
Object& obj = arg.as_object();
|
||||
size_t k = 0;
|
||||
auto length = length_of_array_like(global_object, obj);
|
||||
if (vm.exception())
|
||||
return;
|
||||
|
||||
if (length + k > MAX_ARRAY_LIKE_INDEX) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
|
||||
return;
|
||||
}
|
||||
while (k < length) {
|
||||
auto k_exists = obj.has_property(k);
|
||||
if (vm.exception())
|
||||
return;
|
||||
if (k_exists) {
|
||||
auto k_value = obj.get(k).value_or(js_undefined());
|
||||
if (vm.exception())
|
||||
return;
|
||||
new_array->put(n, k_value);
|
||||
}
|
||||
++n;
|
||||
++k;
|
||||
}
|
||||
} else {
|
||||
if (n >= MAX_ARRAY_LIKE_INDEX) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
|
||||
return;
|
||||
}
|
||||
new_array->put(n, arg);
|
||||
++n;
|
||||
}
|
||||
};
|
||||
|
||||
append_to_new_array(this_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
for (size_t i = 0; i < vm.argument_count(); ++i) {
|
||||
auto argument = vm.argument(i);
|
||||
if (argument.is_array(global_object)) {
|
||||
auto& argument_object = argument.as_object();
|
||||
new_array->indexed_properties().append_all(&argument_object, argument_object.indexed_properties());
|
||||
continue;
|
||||
}
|
||||
append_to_new_array(argument);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
new_array->indexed_properties().append(argument);
|
||||
}
|
||||
|
||||
new_array->put(vm.names.length, Value(n));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
return Value(new_array);
|
||||
}
|
||||
|
||||
|
|
|
@ -203,4 +203,43 @@ describe("ability to work with generic non-array objects", () => {
|
|||
0: "foo",
|
||||
});
|
||||
});
|
||||
|
||||
test("concat", () => {
|
||||
expect(Array.prototype.concat.call(o)).toEqual([o]);
|
||||
expect(Array.prototype.concat.call(o, true)).toEqual([o, true]);
|
||||
expect(Array.prototype.concat.call({}, o)).toEqual([{}, o]);
|
||||
|
||||
const spreadable = {
|
||||
length: 5,
|
||||
0: "foo",
|
||||
1: "bar",
|
||||
3: "baz",
|
||||
[Symbol.isConcatSpreadable]: true,
|
||||
};
|
||||
expect(Array.prototype.concat.call(spreadable)).toEqual([
|
||||
"foo",
|
||||
"bar",
|
||||
undefined,
|
||||
"baz",
|
||||
undefined,
|
||||
]);
|
||||
expect(Array.prototype.concat.call(spreadable, [1, 2])).toEqual([
|
||||
"foo",
|
||||
"bar",
|
||||
undefined,
|
||||
"baz",
|
||||
undefined,
|
||||
1,
|
||||
2,
|
||||
]);
|
||||
expect(Array.prototype.concat.call([1], spreadable, [2])).toEqual([
|
||||
1,
|
||||
"foo",
|
||||
"bar",
|
||||
undefined,
|
||||
"baz",
|
||||
undefined,
|
||||
2,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue