From 77c2db41835294d60d01396865f0c9c81e7378c3 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Sat, 12 Jun 2021 05:37:09 +0300 Subject: LibJS: Add all of the WeakMap.prototype methods (delete, get, has, set) --- .../Libraries/LibJS/Runtime/WeakMapPrototype.cpp | 58 ++++++++++++++++++++++ .../Libraries/LibJS/Runtime/WeakMapPrototype.h | 5 ++ .../LibJS/Tests/builtins/WeakMap/WeakMap.js | 34 +++++++++++++ .../builtins/WeakMap/WeakMap.prototype.delete.js | 13 +++++ .../builtins/WeakMap/WeakMap.prototype.get.js | 12 +++++ .../builtins/WeakMap/WeakMap.prototype.has.js | 16 ++++++ .../builtins/WeakMap/WeakMap.prototype.set.js | 30 +++++++++++ Userland/Libraries/LibTest/JavaScriptTestRunner.h | 1 + 8 files changed, 169 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.delete.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.get.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.has.js create mode 100644 Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js (limited to 'Userland/Libraries') diff --git a/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.cpp b/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.cpp index 892b1c14b6..6b63c47115 100644 --- a/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.cpp @@ -18,6 +18,12 @@ void WeakMapPrototype::initialize(GlobalObject& global_object) { auto& vm = this->vm(); Object::initialize(global_object); + u8 attr = Attribute::Writable | Attribute::Configurable; + + define_native_function(vm.names.delete_, delete_, 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_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), vm.names.WeakMap), Attribute::Configurable); } @@ -38,4 +44,56 @@ WeakMap* WeakMapPrototype::typed_this(VM& vm, GlobalObject& global_object) return static_cast(this_object); } +JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::delete_) +{ + auto* weak_map = typed_this(vm, global_object); + if (!weak_map) + return {}; + auto value = vm.argument(0); + if (!value.is_object()) + return Value(false); + return Value(weak_map->values().remove(&value.as_object())); +} + +JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::get) +{ + auto* weak_map = typed_this(vm, global_object); + if (!weak_map) + return {}; + auto value = vm.argument(0); + if (!value.is_object()) + return js_undefined(); + auto& values = weak_map->values(); + auto result = values.find(&value.as_object()); + if (result == values.end()) + return js_undefined(); + return result->value; +} + +JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::has) +{ + auto* weak_map = typed_this(vm, global_object); + if (!weak_map) + return {}; + auto value = vm.argument(0); + if (!value.is_object()) + return Value(false); + auto& values = weak_map->values(); + return Value(values.find(&value.as_object()) != values.end()); +} + +JS_DEFINE_NATIVE_FUNCTION(WeakMapPrototype::set) +{ + auto* weak_map = typed_this(vm, global_object); + if (!weak_map) + return {}; + auto value = vm.argument(0); + if (!value.is_object()) { + vm.throw_exception(global_object, ErrorType::NotAnObject, value.to_string_without_side_effects()); + return {}; + } + weak_map->values().set(&value.as_object(), vm.argument(1)); + return weak_map; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.h b/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.h index 267ddeb689..f662f62eb2 100644 --- a/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/WeakMapPrototype.h @@ -20,6 +20,11 @@ public: private: static WeakMap* typed_this(VM&, GlobalObject&); + + JS_DECLARE_NATIVE_FUNCTION(delete_); + JS_DECLARE_NATIVE_FUNCTION(get); + JS_DECLARE_NATIVE_FUNCTION(has); + JS_DECLARE_NATIVE_FUNCTION(set); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.js b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.js new file mode 100644 index 0000000000..9dcbe59fb9 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.js @@ -0,0 +1,34 @@ +test("constructor properties", () => { + expect(WeakMap).toHaveLength(0); + expect(WeakMap.name).toBe("WeakMap"); +}); + +describe("errors", () => { + test("invalid array iterators", () => { + [-100, Infinity, NaN, {}, 152n].forEach(value => { + expect(() => { + new WeakMap(value); + }).toThrowWithMessage(TypeError, "is not iterable"); + }); + }); + test("called without new", () => { + expect(() => { + WeakMap(); + }).toThrowWithMessage(TypeError, "WeakMap constructor must be called with 'new'"); + }); +}); + +describe("normal behavior", () => { + test("typeof", () => { + expect(typeof new WeakMap()).toBe("object"); + }); + + test("constructor with single array of entries argument", () => { + var a = new WeakMap([ + [{ a: 1 }, 1], + [{ a: 2 }, 2], + [{ a: 3 }, 3], + ]); + expect(a instanceof WeakMap).toBeTrue(); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.delete.js b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.delete.js new file mode 100644 index 0000000000..dd12bd8b1a --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.delete.js @@ -0,0 +1,13 @@ +test("basic functionality", () => { + expect(WeakMap.prototype.delete).toHaveLength(1); + + var original = [ + [{ a: 1 }, 1], + [{ a: 2 }, 2], + [{ a: 3 }, 3], + ]; + const weakMap = new WeakMap(original); + expect(weakMap.delete(original[0][0])).toBeTrue(); + expect(weakMap.delete(original[0][0])).toBeFalse(); + expect(weakMap.delete(null)).toBeFalse(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.get.js b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.get.js new file mode 100644 index 0000000000..09cd06c970 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.get.js @@ -0,0 +1,12 @@ +test("basic functionality", () => { + expect(WeakMap.prototype.get).toHaveLength(1); + + var original = [ + [{ a: 1 }, 1], + [{ a: 2 }, 2], + [{ a: 3 }, 3], + ]; + const weakMap = new WeakMap(original); + expect(weakMap.get(original[0][0])).toBe(original[0][1]); + expect(weakMap.get(null)).toBe(undefined); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.has.js b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.has.js new file mode 100644 index 0000000000..b0d67baa54 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.has.js @@ -0,0 +1,16 @@ +test("length is 1", () => { + expect(WeakMap.prototype.has).toHaveLength(1); +}); + +test("basic functionality", () => { + var original = [ + [{ a: 1 }, 1], + [{ a: 2 }, 2], + [{ a: 3 }, 3], + ]; + var weakMap = new WeakMap(original); + + expect(new WeakMap().has()).toBeFalse(); + expect(weakMap.has(original[0][0])).toBeTrue(); + expect(weakMap.has({ a: 1 })).toBeFalse(); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js new file mode 100644 index 0000000000..f7e422da8e --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/WeakMap/WeakMap.prototype.set.js @@ -0,0 +1,30 @@ +test("basic functionality", () => { + expect(WeakMap.prototype.set).toHaveLength(2); + + const weakMap = new WeakMap([ + [{ a: 1 }, 1], + [{ a: 2 }, 2], + [{ a: 3 }, 3], + ]); + expect(weakMap.set({ a: 4 }, 4)).toBe(weakMap); + expect(weakMap.set({ a: 1 }, 2)).toBe(weakMap); +}); + +test("invalid values", () => { + const weakMap = new WeakMap(); + [-100, Infinity, NaN, "hello", 152n].forEach(value => { + expect(() => { + weakMap.set(value, value); + }).toThrowWithMessage(TypeError, "is not an object"); + }); +}); + +test("automatic removal of garbage-collected values", () => { + const weakMap = new WeakMap(); + { + expect(weakMap.set({ a: 1 }, 1)).toBe(weakMap); + expect(getWeakMapSize(weakMap)).toBe(1); + } + gc(); + expect(getWeakMapSize(weakMap)).toBe(0); +}); diff --git a/Userland/Libraries/LibTest/JavaScriptTestRunner.h b/Userland/Libraries/LibTest/JavaScriptTestRunner.h index 57ce930e3b..a810cee517 100644 --- a/Userland/Libraries/LibTest/JavaScriptTestRunner.h +++ b/Userland/Libraries/LibTest/JavaScriptTestRunner.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3