summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2021-06-12 23:57:01 +0300
committerLinus Groh <mail@linusgroh.de>2021-06-13 00:33:18 +0100
commit6c0d5163a1bdbfab120525f075830a023500d8dc (patch)
treedc6c6f4dc17d98bade7fb6786019a8c607a188b6
parenta96ac8bd56f5a06552ed2a6fe7b21388d5454250 (diff)
downloadserenity-6c0d5163a1bdbfab120525f075830a023500d8dc.zip
LibJS: Add most of the Map.prototype methods
Specifically all aside from "keys", "values" and "entries" which require an implementation of the MapIterator object.
-rw-r--r--Userland/Libraries/LibJS/Runtime/MapPrototype.cpp81
-rw-r--r--Userland/Libraries/LibJS/Runtime/MapPrototype.h7
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.clear.js12
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.delete.js14
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.forEach.js56
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js11
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.has.js17
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.set.js14
8 files changed, 212 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/MapPrototype.cpp b/Userland/Libraries/LibJS/Runtime/MapPrototype.cpp
index dc1b8da235..b5fb37757f 100644
--- a/Userland/Libraries/LibJS/Runtime/MapPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/MapPrototype.cpp
@@ -18,6 +18,14 @@ void MapPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+
+ define_native_function(vm.names.clear, clear, 0, attr);
+ define_native_function(vm.names.delete_, delete_, 1, attr);
+ define_native_function(vm.names.forEach, for_each, 1, attr);
+ define_native_function(vm.names.get, get, 1, attr);
+ define_native_function(vm.names.has, has, 1, attr);
+ define_native_function(vm.names.set, set, 2, attr);
define_native_accessor(vm.names.size, size_getter, {}, Attribute::Configurable);
@@ -40,6 +48,79 @@ Map* MapPrototype::typed_this(VM& vm, GlobalObject& global_object)
return static_cast<Map*>(this_object);
}
+// 24.1.3.1 Map.prototype.clear ( ), https://tc39.es/ecma262/#sec-map.prototype.clear
+JS_DEFINE_NATIVE_FUNCTION(MapPrototype::clear)
+{
+ auto* map = typed_this(vm, global_object);
+ if (!map)
+ return {};
+ map->entries().clear();
+ return js_undefined();
+}
+
+// 24.1.3.3 Map.prototype.delete ( key ), https://tc39.es/ecma262/#sec-map.prototype.delete
+JS_DEFINE_NATIVE_FUNCTION(MapPrototype::delete_)
+{
+ auto* map = typed_this(vm, global_object);
+ if (!map)
+ return {};
+ return Value(map->entries().remove(vm.argument(0)));
+}
+
+// 24.1.3.5 Map.prototype.forEach ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-map.prototype.foreach
+JS_DEFINE_NATIVE_FUNCTION(MapPrototype::for_each)
+{
+ auto* map = typed_this(vm, global_object);
+ if (!map)
+ return {};
+ if (!vm.argument(0).is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, vm.argument(0).to_string_without_side_effects());
+ return {};
+ }
+ auto this_value = vm.this_value(global_object);
+ for (auto& entry : map->entries()) {
+ (void)vm.call(vm.argument(0).as_function(), vm.argument(1), entry.value, entry.key, this_value);
+ if (vm.exception())
+ return {};
+ }
+ return js_undefined();
+}
+
+// 24.1.3.6 Map.prototype.get ( key ), https://tc39.es/ecma262/#sec-map.prototype.get
+JS_DEFINE_NATIVE_FUNCTION(MapPrototype::get)
+{
+ auto* map = typed_this(vm, global_object);
+ if (!map)
+ return {};
+ auto result = map->entries().get(vm.argument(0));
+ if (!result.has_value())
+ return js_undefined();
+ return result.value();
+}
+
+// 24.1.3.7 Map.prototype.has ( key ), https://tc39.es/ecma262/#sec-map.prototype.has
+JS_DEFINE_NATIVE_FUNCTION(MapPrototype::has)
+{
+ auto* map = typed_this(vm, global_object);
+ if (!map)
+ return {};
+ auto& entries = map->entries();
+ return Value(entries.find(vm.argument(0)) != entries.end());
+}
+
+// 24.1.3.9 Map.prototype.set ( key, value ), https://tc39.es/ecma262/#sec-map.prototype.set
+JS_DEFINE_NATIVE_FUNCTION(MapPrototype::set)
+{
+ auto* map = typed_this(vm, global_object);
+ if (!map)
+ return {};
+ auto key = vm.argument(0);
+ if (key.is_negative_zero())
+ key = Value(0);
+ map->entries().set(key, vm.argument(1));
+ return map;
+}
+
// 24.1.3.10 get Map.prototype.size, https://tc39.es/ecma262/#sec-get-map.prototype.size
JS_DEFINE_NATIVE_GETTER(MapPrototype::size_getter)
{
diff --git a/Userland/Libraries/LibJS/Runtime/MapPrototype.h b/Userland/Libraries/LibJS/Runtime/MapPrototype.h
index f11a2bc6c5..ae7b95ba96 100644
--- a/Userland/Libraries/LibJS/Runtime/MapPrototype.h
+++ b/Userland/Libraries/LibJS/Runtime/MapPrototype.h
@@ -21,6 +21,13 @@ public:
private:
static Map* typed_this(VM&, GlobalObject&);
+ JS_DECLARE_NATIVE_FUNCTION(clear);
+ JS_DECLARE_NATIVE_FUNCTION(delete_);
+ JS_DECLARE_NATIVE_FUNCTION(for_each);
+ JS_DECLARE_NATIVE_FUNCTION(get);
+ JS_DECLARE_NATIVE_FUNCTION(has);
+ JS_DECLARE_NATIVE_FUNCTION(set);
+
JS_DECLARE_NATIVE_GETTER(size_getter);
};
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.clear.js b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.clear.js
new file mode 100644
index 0000000000..0120d2f4ca
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.clear.js
@@ -0,0 +1,12 @@
+test("basic functionality", () => {
+ expect(Map.prototype.clear).toHaveLength(0);
+
+ const map = new Map([
+ ["a", 0],
+ ["b", 1],
+ ["c", 2],
+ ]);
+ expect(map).toHaveSize(3);
+ map.clear();
+ expect(map).toHaveSize(0);
+});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.delete.js b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.delete.js
new file mode 100644
index 0000000000..c7d6bd9192
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.delete.js
@@ -0,0 +1,14 @@
+test("basic functionality", () => {
+ expect(Map.prototype.delete).toHaveLength(1);
+
+ const map = new Map([
+ ["a", 0],
+ ["b", 1],
+ ["c", 2],
+ ]);
+ expect(map).toHaveSize(3);
+ expect(map.delete("b")).toBeTrue();
+ expect(map).toHaveSize(2);
+ expect(map.delete("b")).toBeFalse();
+ expect(map).toHaveSize(2);
+});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.forEach.js b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.forEach.js
new file mode 100644
index 0000000000..abfb4ce3e2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.forEach.js
@@ -0,0 +1,56 @@
+test("length is 1", () => {
+ expect(Map.prototype.forEach).toHaveLength(1);
+});
+
+describe("errors", () => {
+ test("requires at least one argument", () => {
+ expect(() => {
+ new Map().forEach();
+ }).toThrowWithMessage(TypeError, "undefined is not a function");
+ });
+
+ test("callback must be a function", () => {
+ expect(() => {
+ new Map().forEach(undefined);
+ }).toThrowWithMessage(TypeError, "undefined is not a function");
+ });
+});
+
+describe("normal behavior", () => {
+ test("never calls callback with empty set", () => {
+ var callbackCalled = 0;
+ expect(
+ new Map().forEach(() => {
+ callbackCalled++;
+ })
+ ).toBeUndefined();
+ expect(callbackCalled).toBe(0);
+ });
+
+ test("calls callback once for every item", () => {
+ var callbackCalled = 0;
+ expect(
+ new Map([
+ ["a", 0],
+ ["b", 1],
+ ["c", 2],
+ ]).forEach(() => {
+ callbackCalled++;
+ })
+ ).toBeUndefined();
+ expect(callbackCalled).toBe(3);
+ });
+
+ test("callback receives value, key and map", () => {
+ var a = new Map([
+ ["a", 0],
+ ["b", 1],
+ ["c", 2],
+ ]);
+ a.forEach((value, key, map) => {
+ expect(a.has(key)).toBeTrue();
+ expect(a.get(key)).toBe(value);
+ expect(map).toBe(a);
+ });
+ });
+});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js
new file mode 100644
index 0000000000..1b99f81cb6
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.get.js
@@ -0,0 +1,11 @@
+test("basic functionality", () => {
+ expect(Map.prototype.get).toHaveLength(1);
+
+ const map = new Map([
+ ["a", 0],
+ ["b", 1],
+ ["c", 2],
+ ]);
+ expect(map.get("a")).toBe(0);
+ expect(map.get("d")).toBe(undefined);
+});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.has.js b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.has.js
new file mode 100644
index 0000000000..744be27975
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.has.js
@@ -0,0 +1,17 @@
+test("length is 1", () => {
+ expect(Map.prototype.has).toHaveLength(1);
+});
+
+test("basic functionality", () => {
+ var map = new Map([
+ ["a", 0],
+ [1, "b"],
+ ["c", 2],
+ ]);
+
+ expect(new Map().has()).toBeFalse();
+ expect(new Map([{}]).has()).toBeTrue();
+ expect(map.has("a")).toBeTrue();
+ expect(map.has(1)).toBeTrue();
+ expect(map.has("serenity")).toBeFalse();
+});
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.set.js b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.set.js
new file mode 100644
index 0000000000..c4c9a4ab07
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Map/Map.prototype.set.js
@@ -0,0 +1,14 @@
+test("basic functionality", () => {
+ expect(Map.prototype.set).toHaveLength(2);
+
+ const map = new Map([
+ ["a", 0],
+ ["b", 1],
+ ["c", 2],
+ ]);
+ expect(map).toHaveSize(3);
+ expect(map.set("d", 3)).toBe(map);
+ expect(map).toHaveSize(4);
+ expect(map.set("a", -1)).toBe(map);
+ expect(map).toHaveSize(4);
+});