/src/serenity/Userland/Libraries/LibJS/Runtime/MapConstructor.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> |
3 | | * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <LibJS/Runtime/AbstractOperations.h> |
9 | | #include <LibJS/Runtime/Array.h> |
10 | | #include <LibJS/Runtime/Error.h> |
11 | | #include <LibJS/Runtime/Iterator.h> |
12 | | #include <LibJS/Runtime/Map.h> |
13 | | #include <LibJS/Runtime/MapConstructor.h> |
14 | | |
15 | | namespace JS { |
16 | | |
17 | | JS_DEFINE_ALLOCATOR(MapConstructor); |
18 | | |
19 | | MapConstructor::MapConstructor(Realm& realm) |
20 | 0 | : NativeFunction(realm.vm().names.Map.as_string(), realm.intrinsics().function_prototype()) |
21 | 0 | { |
22 | 0 | } |
23 | | |
24 | | void MapConstructor::initialize(Realm& realm) |
25 | 0 | { |
26 | 0 | auto& vm = this->vm(); |
27 | 0 | Base::initialize(realm); |
28 | | |
29 | | // 24.1.2.2 Map.prototype, https://tc39.es/ecma262/#sec-map.prototype |
30 | 0 | define_direct_property(vm.names.prototype, realm.intrinsics().map_prototype(), 0); |
31 | |
|
32 | 0 | u8 attr = Attribute::Writable | Attribute::Configurable; |
33 | 0 | define_native_function(realm, vm.names.groupBy, group_by, 2, attr); |
34 | |
|
35 | 0 | define_native_accessor(realm, vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable); |
36 | |
|
37 | 0 | define_direct_property(vm.names.length, Value(0), Attribute::Configurable); |
38 | 0 | } |
39 | | |
40 | | // 24.1.1.1 Map ( [ iterable ] ), https://tc39.es/ecma262/#sec-map-iterable |
41 | | ThrowCompletionOr<Value> MapConstructor::call() |
42 | 0 | { |
43 | 0 | auto& vm = this->vm(); |
44 | 0 | return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, vm.names.Map); |
45 | 0 | } |
46 | | |
47 | | // 24.1.1.1 Map ( [ iterable ] ), https://tc39.es/ecma262/#sec-map-iterable |
48 | | ThrowCompletionOr<NonnullGCPtr<Object>> MapConstructor::construct(FunctionObject& new_target) |
49 | 0 | { |
50 | 0 | auto& vm = this->vm(); |
51 | |
|
52 | 0 | auto map = TRY(ordinary_create_from_constructor<Map>(vm, new_target, &Intrinsics::map_prototype)); |
53 | |
|
54 | 0 | if (vm.argument(0).is_nullish()) |
55 | 0 | return map; |
56 | | |
57 | 0 | auto adder = TRY(map->get(vm.names.set)); |
58 | 0 | if (!adder.is_function()) |
59 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAFunction, "'set' property of Map"); |
60 | | |
61 | 0 | (void)TRY(get_iterator_values(vm, vm.argument(0), [&](Value iterator_value) -> Optional<Completion> { |
62 | 0 | if (!iterator_value.is_object()) |
63 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, ByteString::formatted("Iterator value {}", iterator_value.to_string_without_side_effects())); |
64 | |
|
65 | 0 | auto key = TRY(iterator_value.as_object().get(0)); |
66 | 0 | auto value = TRY(iterator_value.as_object().get(1)); |
67 | 0 | TRY(JS::call(vm, adder.as_function(), map, key, value)); |
68 | |
|
69 | 0 | return {}; |
70 | 0 | })); |
71 | |
|
72 | 0 | return map; |
73 | 0 | } |
74 | | |
75 | | // 24.1.2.1 Map.groupBy ( items, callbackfn ), https://tc39.es/ecma262/#sec-map.groupby |
76 | | JS_DEFINE_NATIVE_FUNCTION(MapConstructor::group_by) |
77 | 0 | { |
78 | 0 | auto& realm = *vm.current_realm(); |
79 | |
|
80 | 0 | auto items = vm.argument(0); |
81 | 0 | auto callback_function = vm.argument(1); |
82 | |
|
83 | 0 | struct KeyedGroupTraits : public Traits<Handle<Value>> { |
84 | 0 | static unsigned hash(Handle<Value> const& value_handle) |
85 | 0 | { |
86 | 0 | return ValueTraits::hash(value_handle.value()); |
87 | 0 | } |
88 | |
|
89 | 0 | static bool equals(Handle<Value> const& a, Handle<Value> const& b) |
90 | 0 | { |
91 | | // AddValueToKeyedGroup uses SameValue on the keys on Step 1.a. |
92 | 0 | return same_value(a.value(), b.value()); |
93 | 0 | } |
94 | 0 | }; |
95 | | |
96 | | // 1. Let groups be ? GroupBy(items, callbackfn, zero). |
97 | 0 | auto groups = TRY((JS::group_by<OrderedHashMap<Handle<Value>, MarkedVector<Value>, KeyedGroupTraits>, void>(vm, items, callback_function))); |
98 | | |
99 | | // 2. Let map be ! Construct(%Map%). |
100 | 0 | auto map = Map::create(realm); |
101 | | |
102 | | // 3. For each Record { [[Key]], [[Elements]] } g of groups, do |
103 | 0 | for (auto& group : groups) { |
104 | | // a. Let elements be CreateArrayFromList(g.[[Elements]]). |
105 | 0 | auto elements = Array::create_from(realm, group.value); |
106 | | |
107 | | // b. Let entry be the Record { [[Key]]: g.[[Key]], [[Value]]: elements }. |
108 | | // c. Append entry to map.[[MapData]]. |
109 | 0 | map->map_set(group.key.value(), elements); |
110 | 0 | } |
111 | | |
112 | | // 4. Return map. |
113 | 0 | return map; |
114 | 0 | } |
115 | | |
116 | | // 24.1.2.3 get Map [ @@species ], https://tc39.es/ecma262/#sec-get-map-@@species |
117 | | JS_DEFINE_NATIVE_FUNCTION(MapConstructor::symbol_species_getter) |
118 | 0 | { |
119 | 0 | return vm.this_value(); |
120 | 0 | } |
121 | | |
122 | | } |