summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp85
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayPrototype.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h1
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js29
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.copyWithin.js45
5 files changed, 161 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
index 9cd731d5cf..8dbc154a38 100644
--- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
@@ -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)
{
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h
index 7d998f03a9..7d392762ec 100644
--- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h
+++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h
@@ -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);
};
}
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
index 394b43ca26..2999509eb9 100644
--- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
+++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -82,6 +82,7 @@ namespace JS {
P(console) \
P(construct) \
P(constructor) \
+ P(copyWithin) \
P(cos) \
P(cosh) \
P(count) \
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js
index af1b189834..c509ea3056 100644
--- a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js
+++ b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype-generic-functions.js
@@ -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", () => {
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.copyWithin.js b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.copyWithin.js
new file mode 100644
index 0000000000..831269574a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.copyWithin.js
@@ -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]);
+ });
+});