diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp index 5ee62d2ea2..5b9eaf1df8 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp @@ -29,6 +29,7 @@ void TypedArrayPrototype::initialize(GlobalObject& object) define_native_function(vm.names.every, every, 1, attr); define_native_function(vm.names.find, find, 1, attr); define_native_function(vm.names.findIndex, find_index, 1, attr); + define_native_function(vm.names.forEach, for_each, 1, attr); } TypedArrayPrototype::~TypedArrayPrototype() @@ -169,6 +170,15 @@ JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::find_index) return Value(result_index); } +// 23.2.3.12 %TypedArray%.prototype.forEach ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach +JS_DEFINE_NATIVE_FUNCTION(TypedArrayPrototype::for_each) +{ + for_each_item(vm, global_object, "forEach", [](auto, auto, auto) { + return IterationDecision::Continue; + }); + return js_undefined(); +} + // 23.2.3.1 get %TypedArray%.prototype.buffer, https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer JS_DEFINE_NATIVE_GETTER(TypedArrayPrototype::buffer_getter) { diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h index 3763841295..0830745140 100644 --- a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h @@ -28,6 +28,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(every); JS_DECLARE_NATIVE_FUNCTION(find); JS_DECLARE_NATIVE_FUNCTION(find_index); + JS_DECLARE_NATIVE_FUNCTION(for_each); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.forEach.js b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.forEach.js new file mode 100644 index 0000000000..aa79ea07b3 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/TypedArray/TypedArray.prototype.forEach.js @@ -0,0 +1,145 @@ +const TYPED_ARRAYS = [ + Uint8Array, + Uint16Array, + Uint32Array, + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, +]; + +const BIGINT_TYPED_ARRAYS = [BigUint64Array, BigInt64Array]; + +test("length is 1", () => { + TYPED_ARRAYS.forEach(T => { + expect(T.prototype.forEach).toHaveLength(1); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + expect(T.prototype.forEach).toHaveLength(1); + }); +}); + +describe("errors", () => { + function errorTests(T) { + test(`requires at least one argument (${T.name})`, () => { + expect(() => { + new T().forEach(); + }).toThrowWithMessage( + TypeError, + "TypedArray.prototype.forEach() requires at least one argument" + ); + }); + + test(`callback must be a function (${T.name})`, () => { + expect(() => { + new T().forEach(undefined); + }).toThrowWithMessage(TypeError, "undefined is not a function"); + }); + } + + TYPED_ARRAYS.forEach(T => errorTests(T)); + BIGINT_TYPED_ARRAYS.forEach(T => errorTests(T)); +}); + +describe("normal behaviour", () => { + test("never calls callback with empty array", () => { + function emptyTest(T) { + var callbackCalled = 0; + expect( + new T().forEach(() => { + callbackCalled++; + }) + ).toBeUndefined(); + expect(callbackCalled).toBe(0); + } + + TYPED_ARRAYS.forEach(T => emptyTest(T)); + BIGINT_TYPED_ARRAYS.forEach(T => emptyTest(T)); + }); + + test("calls callback once for every item", () => { + TYPED_ARRAYS.forEach(T => { + var callbackCalled = 0; + expect( + new T([1, 2, 3]).forEach(() => { + callbackCalled++; + }) + ).toBeUndefined(); + expect(callbackCalled).toBe(3); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + var callbackCalled = 0; + expect( + new T([1n, 2n, 3n]).forEach(() => { + callbackCalled++; + }) + ).toBeUndefined(); + expect(callbackCalled).toBe(3); + }); + }); + + test("callback receives value and index", () => { + TYPED_ARRAYS.forEach(T => { + const typedArray = new T([1, 2, 3]); + typedArray.forEach((value, index) => { + expect(value).toBe(typedArray[index]); + expect(index).toBe(typedArray[index] - 1); + }); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + const typedArray = new T([1n, 2n, 3n]); + typedArray.forEach((value, index) => { + expect(value).toBe(typedArray[index]); + expect(index).toBe(parseInt(typedArray[index] - 1n)); + }); + }); + }); + + test("callback receives array", () => { + TYPED_ARRAYS.forEach(T => { + var callbackCalled = 0; + const typedArray = new T([1, 2, 3]); + typedArray.forEach((_, __, array) => { + callbackCalled++; + expect(typedArray).toEqual(array); + }); + expect(callbackCalled).toBe(3); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + var callbackCalled = 0; + const typedArray = new T([1n, 2n, 3n]); + typedArray.forEach((_, __, array) => { + callbackCalled++; + expect(typedArray).toEqual(array); + }); + expect(callbackCalled).toBe(3); + }); + }); + + test("this value can be modified", () => { + TYPED_ARRAYS.forEach(T => { + const typedArray = new T([1, 2, 3]); + [4, 5, 6].forEach(function (value, index) { + this[index] = value; + }, typedArray); + expect(typedArray[0]).toBe(4); + expect(typedArray[1]).toBe(5); + expect(typedArray[2]).toBe(6); + }); + + BIGINT_TYPED_ARRAYS.forEach(T => { + const typedArray = new T([1n, 2n, 3n]); + [4n, 5n, 6n].forEach(function (value, index) { + this[index] = value; + }, typedArray); + expect(typedArray[0]).toBe(4n); + expect(typedArray[1]).toBe(5n); + expect(typedArray[2]).toBe(6n); + }); + }); +});