diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp index 311c1f9248..9cd731d5cf 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp @@ -64,6 +64,7 @@ void ArrayPrototype::initialize(GlobalObject& global_object) define_native_function(vm.names.flatMap, flat_map, 1, attr); 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); // Use define_property here instead of define_native_function so that // Object.is(Array.prototype[Symbol.iterator], Array.prototype.values) @@ -1264,6 +1265,16 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::values) return ArrayIterator::create(global_object, this_object, Object::PropertyKind::Value); } +// 23.1.3.16 Array.prototype.entries ( ), https://tc39.es/ecma262/#sec-array.prototype.entries +JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::entries) +{ + auto* this_object = vm.this_value(global_object).to_object(global_object); + if (!this_object) + return {}; + + return ArrayIterator::create(global_object, this_object, Object::PropertyKind::KeyAndValue); +} + // 23.1.3.16 Array.prototype.keys ( ), https://tc39.es/ecma262/#sec-array.prototype.keys JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::keys) { diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h index 4a01777496..7d998f03a9 100644 --- a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h @@ -50,6 +50,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(flat_map); JS_DECLARE_NATIVE_FUNCTION(at); JS_DECLARE_NATIVE_FUNCTION(keys); + JS_DECLARE_NATIVE_FUNCTION(entries); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.entries.js b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.entries.js new file mode 100644 index 0000000000..8c1f0be823 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Array/Array.prototype.entries.js @@ -0,0 +1,44 @@ +test("length", () => { + expect(Array.prototype.entries.length).toBe(0); +}); + +test("basic functionality", () => { + const a = ["a", "b", "c"]; + const it = a.entries(); + expect(it.next()).toEqual({ value: [0, "a"], done: false }); + expect(it.next()).toEqual({ value: [1, "b"], done: false }); + expect(it.next()).toEqual({ value: [2, "c"], done: false }); + expect(it.next()).toEqual({ value: undefined, done: true }); + expect(it.next()).toEqual({ value: undefined, done: true }); + expect(it.next()).toEqual({ value: undefined, done: true }); +}); + +test("works when applied to non-object", () => { + [true, false, 9, 2n, Symbol()].forEach(primitive => { + const it = [].entries.call(primitive); + expect(it.next()).toEqual({ value: undefined, done: true }); + expect(it.next()).toEqual({ value: undefined, done: true }); + expect(it.next()).toEqual({ value: undefined, done: true }); + }); +}); + +test("item added to array before exhaustion is accessible", () => { + const a = ["a", "b"]; + const it = a.entries(); + expect(it.next()).toEqual({ value: [0, "a"], done: false }); + expect(it.next()).toEqual({ value: [1, "b"], done: false }); + a.push("c"); + expect(it.next()).toEqual({ value: [2, "c"], done: false }); + expect(it.next()).toEqual({ value: undefined, done: true }); + expect(it.next()).toEqual({ value: undefined, done: true }); +}); + +test("item added to array after exhaustion is inaccessible", () => { + const a = ["a", "b"]; + const it = a.entries(); + expect(it.next()).toEqual({ value: [0, "a"], done: false }); + expect(it.next()).toEqual({ value: [1, "b"], done: false }); + expect(it.next()).toEqual({ value: undefined, done: true }); + a.push("c"); + expect(it.next()).toEqual({ value: undefined, done: true }); +});