mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 07:07:34 +00:00
LibJS: Add most of the Map.prototype methods
Specifically all aside from "keys", "values" and "entries" which require an implementation of the MapIterator object.
This commit is contained in:
parent
a96ac8bd56
commit
6c0d5163a1
8 changed files with 212 additions and 0 deletions
|
@ -18,6 +18,14 @@ void MapPrototype::initialize(GlobalObject& global_object)
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
Object::initialize(global_object);
|
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);
|
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);
|
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
|
// 24.1.3.10 get Map.prototype.size, https://tc39.es/ecma262/#sec-get-map.prototype.size
|
||||||
JS_DEFINE_NATIVE_GETTER(MapPrototype::size_getter)
|
JS_DEFINE_NATIVE_GETTER(MapPrototype::size_getter)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,13 @@ public:
|
||||||
private:
|
private:
|
||||||
static Map* typed_this(VM&, GlobalObject&);
|
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);
|
JS_DECLARE_NATIVE_GETTER(size_getter);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
|
@ -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();
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue