mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-30 08:41:15 +00:00
LibJS: Implement Array.prototype.copyWithin generically
This commit is contained in:
parent
417f752306
commit
6c13cc67c6
Notes:
sideshowbarker
2024-07-18 12:16:17 +09:00
Author: https://github.com/davidot Commit: https://github.com/SerenityOS/serenity/commit/6c13cc67c63 Pull-request: https://github.com/SerenityOS/serenity/pull/8025 Reviewed-by: https://github.com/linusg
|
@ -65,6 +65,7 @@ void ArrayPrototype::initialize(GlobalObject& global_object)
|
|||
define_native_function(vm.names.at, at, 1, attr);
|
||||
define_native_function(vm.names.keys, keys, 0, attr);
|
||||
define_native_function(vm.names.entries, entries, 0, attr);
|
||||
define_native_function(vm.names.copyWithin, copy_within, 2, attr);
|
||||
|
||||
// Use define_property here instead of define_native_function so that
|
||||
// Object.is(Array.prototype[Symbol.iterator], Array.prototype.values)
|
||||
|
@ -1378,6 +1379,90 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::flat_map)
|
|||
return new_array;
|
||||
}
|
||||
|
||||
// 23.1.3.3 Array.prototype.copyWithin ( target, start [ , end ] ), https://tc39.es/ecma262/#sec-array.prototype.copywithin
|
||||
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::copy_within)
|
||||
{
|
||||
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||
if (!this_object)
|
||||
return {};
|
||||
auto length = length_of_array_like(global_object, *this_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
auto relative_target = vm.argument(0).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
double to;
|
||||
if (relative_target < 0)
|
||||
to = max(length + relative_target, 0.0);
|
||||
else
|
||||
to = min(relative_target, (double)length);
|
||||
|
||||
auto relative_start = vm.argument(1).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
double from;
|
||||
if (relative_start < 0)
|
||||
from = max(length + relative_start, 0.0);
|
||||
else
|
||||
from = min(relative_start, (double)length);
|
||||
|
||||
auto relative_end = vm.argument(2).is_undefined() ? length : vm.argument(2).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
double final;
|
||||
if (relative_end < 0)
|
||||
final = max(length + relative_end, 0.0);
|
||||
else
|
||||
final = min(relative_end, (double)length);
|
||||
|
||||
double count = min(final - from, length - to);
|
||||
|
||||
i32 direction = 1;
|
||||
|
||||
if (from < to && to < from + count) {
|
||||
direction = -1;
|
||||
from = from + count - 1;
|
||||
to = to + count - 1;
|
||||
}
|
||||
|
||||
if (count < 0) {
|
||||
return this_object;
|
||||
}
|
||||
|
||||
size_t from_i = from;
|
||||
size_t to_i = to;
|
||||
size_t count_i = count;
|
||||
|
||||
while (count_i > 0) {
|
||||
auto from_present = this_object->has_property(from_i);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
if (from_present) {
|
||||
auto from_value = this_object->get(from_i).value_or(js_undefined());
|
||||
if (vm.exception())
|
||||
return {};
|
||||
this_object->put(to_i, from_value);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
} else {
|
||||
this_object->delete_property(to_i);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
}
|
||||
|
||||
from_i += direction;
|
||||
to_i += direction;
|
||||
--count_i;
|
||||
}
|
||||
|
||||
return this_object;
|
||||
}
|
||||
|
||||
// 1.1 Array.prototype.at ( index ), https://tc39.es/proposal-relative-indexing-method/#sec-array.prototype.at
|
||||
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::at)
|
||||
{
|
||||
|
|
|
@ -51,6 +51,7 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(at);
|
||||
JS_DECLARE_NATIVE_FUNCTION(keys);
|
||||
JS_DECLARE_NATIVE_FUNCTION(entries);
|
||||
JS_DECLARE_NATIVE_FUNCTION(copy_within);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ namespace JS {
|
|||
P(console) \
|
||||
P(construct) \
|
||||
P(constructor) \
|
||||
P(copyWithin) \
|
||||
P(cos) \
|
||||
P(cosh) \
|
||||
P(count) \
|
||||
|
|
|
@ -130,6 +130,35 @@ describe("ability to work with generic non-array objects", () => {
|
|||
}
|
||||
});
|
||||
|
||||
test("copyWithin", () => {
|
||||
const initial_o = { length: 5, 0: "foo", 1: "bar", 3: "baz" };
|
||||
{
|
||||
const o = { length: 5, 0: "foo", 1: "bar", 3: "baz" };
|
||||
// returns value and modifies
|
||||
expect(Array.prototype.copyWithin.call(o, 0, 0)).toEqual(o);
|
||||
expect(o).toEqual(initial_o);
|
||||
}
|
||||
|
||||
{
|
||||
const o = {};
|
||||
expect(Array.prototype.copyWithin.call(o, 1, 16, 32)).toEqual(o);
|
||||
expect(o).toEqual({});
|
||||
}
|
||||
|
||||
{
|
||||
const o = { length: 100 };
|
||||
expect(Array.prototype.copyWithin.call(o, 1, 16, 32)).toEqual(o);
|
||||
expect(o).toEqual({ length: 100 });
|
||||
}
|
||||
|
||||
{
|
||||
const o = { length: 5, 0: "foo", 1: "bar", 3: "baz" };
|
||||
// returns value and modifies
|
||||
expect(Array.prototype.copyWithin.call(o, 2, 0)).toEqual(o);
|
||||
expect(o).toEqual({ length: 5, 0: "foo", 1: "bar", 2: "foo", 3: "bar" });
|
||||
}
|
||||
});
|
||||
|
||||
const o = { length: 5, 0: "foo", 1: "bar", 3: "baz" };
|
||||
|
||||
test("every", () => {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
test("length is 2", () => {
|
||||
expect(Array.prototype.copyWithin).toHaveLength(2);
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
test("Noop", () => {
|
||||
var array = [1, 2];
|
||||
array.copyWithin(0, 0);
|
||||
expect(array).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
test("basic behavior", () => {
|
||||
var array = [1, 2, 3];
|
||||
|
||||
var b = array.copyWithin(1, 2);
|
||||
expect(b).toEqual(array);
|
||||
expect(array).toEqual([1, 3, 3]);
|
||||
|
||||
b = array.copyWithin(2, 0);
|
||||
expect(b).toEqual(array);
|
||||
expect(array).toEqual([1, 3, 1]);
|
||||
});
|
||||
|
||||
test("start > target", () => {
|
||||
var array = [1, 2, 3];
|
||||
var b = array.copyWithin(0, 1);
|
||||
expect(b).toEqual(array);
|
||||
expect(array).toEqual([2, 3, 3]);
|
||||
});
|
||||
|
||||
test("overwriting behavior", () => {
|
||||
var array = [1, 2, 3];
|
||||
var b = array.copyWithin(1, 0);
|
||||
expect(b).toEqual(array);
|
||||
expect(array).toEqual([1, 1, 2]);
|
||||
});
|
||||
|
||||
test("specify end", () => {
|
||||
var array = [1, 2, 3];
|
||||
|
||||
b = array.copyWithin(2, 0, 1);
|
||||
expect(b).toEqual(array);
|
||||
expect(array).toEqual([1, 2, 1]);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue