Coverage Report

Created: 2025-12-11 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/lib/VM/JSLib/Map.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
//===----------------------------------------------------------------------===//
9
/// \file
10
/// ES6.0 23.1 Initialize the Map constructor.
11
//===----------------------------------------------------------------------===//
12
13
#include "JSLibInternal.h"
14
15
#include "hermes/VM/StringPrimitive.h"
16
17
namespace hermes {
18
namespace vm {
19
20
94
Handle<JSObject> createMapConstructor(Runtime &runtime) {
21
94
  auto mapPrototype = Handle<JSObject>::vmcast(&runtime.mapPrototype);
22
23
  // Map.prototype.xxx methods.
24
94
  defineMethod(
25
94
      runtime,
26
94
      mapPrototype,
27
94
      Predefined::getSymbolID(Predefined::clear),
28
94
      nullptr,
29
94
      mapPrototypeClear,
30
94
      0);
31
32
94
  defineMethod(
33
94
      runtime,
34
94
      mapPrototype,
35
94
      Predefined::getSymbolID(Predefined::deleteStr),
36
94
      nullptr,
37
94
      mapPrototypeDelete,
38
94
      1);
39
40
94
  defineMethod(
41
94
      runtime,
42
94
      mapPrototype,
43
94
      Predefined::getSymbolID(Predefined::entries),
44
94
      nullptr,
45
94
      mapPrototypeEntries,
46
94
      0);
47
48
94
  defineMethod(
49
94
      runtime,
50
94
      mapPrototype,
51
94
      Predefined::getSymbolID(Predefined::forEach),
52
94
      nullptr,
53
94
      mapPrototypeForEach,
54
94
      1);
55
56
94
  defineMethod(
57
94
      runtime,
58
94
      mapPrototype,
59
94
      Predefined::getSymbolID(Predefined::get),
60
94
      nullptr,
61
94
      mapPrototypeGet,
62
94
      1);
63
64
94
  defineMethod(
65
94
      runtime,
66
94
      mapPrototype,
67
94
      Predefined::getSymbolID(Predefined::has),
68
94
      nullptr,
69
94
      mapPrototypeHas,
70
94
      1);
71
72
94
  defineMethod(
73
94
      runtime,
74
94
      mapPrototype,
75
94
      Predefined::getSymbolID(Predefined::keys),
76
94
      nullptr,
77
94
      mapPrototypeKeys,
78
94
      0);
79
80
94
  defineMethod(
81
94
      runtime,
82
94
      mapPrototype,
83
94
      Predefined::getSymbolID(Predefined::set),
84
94
      nullptr,
85
94
      mapPrototypeSet,
86
94
      2);
87
88
94
  defineAccessor(
89
94
      runtime,
90
94
      mapPrototype,
91
94
      Predefined::getSymbolID(Predefined::size),
92
94
      nullptr,
93
94
      mapPrototypeSizeGetter,
94
94
      nullptr,
95
94
      false,
96
94
      true);
97
98
94
  defineMethod(
99
94
      runtime,
100
94
      mapPrototype,
101
94
      Predefined::getSymbolID(Predefined::values),
102
94
      nullptr,
103
94
      mapPrototypeValues,
104
94
      0);
105
106
94
  DefinePropertyFlags dpf = DefinePropertyFlags::getNewNonEnumerableFlags();
107
108
94
  PseudoHandle<> propValue = runtime.ignoreAllocationFailure(
109
94
      JSObject::getNamed_RJS(
110
94
          mapPrototype, runtime, Predefined::getSymbolID(Predefined::entries)));
111
94
  runtime.ignoreAllocationFailure(
112
94
      JSObject::defineOwnProperty(
113
94
          mapPrototype,
114
94
          runtime,
115
94
          Predefined::getSymbolID(Predefined::SymbolIterator),
116
94
          dpf,
117
94
          runtime.makeHandle<NativeFunction>(propValue.get())));
118
119
94
  dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
120
94
  dpf.writable = 0;
121
94
  dpf.enumerable = 0;
122
94
  defineProperty(
123
94
      runtime,
124
94
      mapPrototype,
125
94
      Predefined::getSymbolID(Predefined::SymbolToStringTag),
126
94
      runtime.getPredefinedStringHandle(Predefined::Map),
127
94
      dpf);
128
129
94
  auto cons = defineSystemConstructor<JSMap>(
130
94
      runtime,
131
94
      Predefined::getSymbolID(Predefined::Map),
132
94
      mapConstructor,
133
94
      mapPrototype,
134
94
      0,
135
94
      CellKind::JSMapKind);
136
137
94
  return cons;
138
94
}
139
140
CallResult<HermesValue>
141
0
mapConstructor(void *, Runtime &runtime, NativeArgs args) {
142
0
  GCScope gcScope{runtime};
143
0
  if (LLVM_UNLIKELY(!args.isConstructorCall())) {
144
0
    return runtime.raiseTypeError("Constructor Map requires 'new'");
145
0
  }
146
0
  auto selfHandle = args.dyncastThis<JSMap>();
147
0
  if (LLVM_UNLIKELY(!selfHandle)) {
148
0
    return runtime.raiseTypeError("Map Constructor only applies to Map object");
149
0
  }
150
0
  JSMap::initializeStorage(selfHandle, runtime);
151
0
  if (args.getArgCount() == 0 || args.getArg(0).isUndefined() ||
152
0
      args.getArg(0).isNull()) {
153
0
    return selfHandle.getHermesValue();
154
0
  }
155
156
0
  auto propRes = JSObject::getNamed_RJS(
157
0
      selfHandle, runtime, Predefined::getSymbolID(Predefined::set));
158
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
159
0
    return ExecutionStatus::EXCEPTION;
160
0
  }
161
162
  // ES6.0 23.1.1.1.7: Cache adder across all iterations of the loop.
163
0
  auto adder =
164
0
      Handle<Callable>::dyn_vmcast(runtime.makeHandle(std::move(*propRes)));
165
0
  if (!adder) {
166
0
    return runtime.raiseTypeError("Property 'set' for Map is not callable");
167
0
  }
168
169
0
  return addEntriesFromIterable(
170
0
      runtime,
171
0
      selfHandle,
172
0
      args.getArgHandle(0),
173
0
      [&runtime, selfHandle, adder](Runtime &, Handle<> key, Handle<> value) {
174
0
        return Callable::executeCall2(
175
0
                   adder,
176
0
                   runtime,
177
0
                   selfHandle,
178
0
                   key.getHermesValue(),
179
0
                   value.getHermesValue())
180
0
            .getStatus();
181
0
      });
182
0
}
183
184
CallResult<HermesValue>
185
0
mapPrototypeClear(void *, Runtime &runtime, NativeArgs args) {
186
0
  auto selfHandle = args.dyncastThis<JSMap>();
187
0
  if (LLVM_UNLIKELY(!selfHandle)) {
188
0
    return runtime.raiseTypeError(
189
0
        "Non-Map object called on Map.prototype.clear");
190
0
  }
191
0
  JSMap::clear(selfHandle, runtime);
192
0
  return HermesValue::encodeUndefinedValue();
193
0
}
194
195
CallResult<HermesValue>
196
0
mapPrototypeDelete(void *, Runtime &runtime, NativeArgs args) {
197
0
  auto selfHandle = args.dyncastThis<JSMap>();
198
0
  if (LLVM_UNLIKELY(!selfHandle)) {
199
0
    return runtime.raiseTypeError(
200
0
        "Non-Map object called on Map.prototype.delete");
201
0
  }
202
0
  return HermesValue::encodeBoolValue(
203
0
      JSMap::deleteKey(selfHandle, runtime, args.getArgHandle(0)));
204
0
}
205
206
CallResult<HermesValue>
207
0
mapPrototypeEntries(void *, Runtime &runtime, NativeArgs args) {
208
0
  auto selfHandle = args.dyncastThis<JSMap>();
209
0
  if (LLVM_UNLIKELY(!selfHandle)) {
210
0
    return runtime.raiseTypeError(
211
0
        "Non-Map object called on Map.prototype.entries");
212
0
  }
213
0
  auto iterator = runtime.makeHandle(
214
0
      JSMapIterator::create(
215
0
          runtime, Handle<JSObject>::vmcast(&runtime.mapIteratorPrototype)));
216
0
  iterator->initializeIterator(runtime, selfHandle, IterationKind::Entry);
217
0
  return iterator.getHermesValue();
218
0
}
219
220
CallResult<HermesValue>
221
0
mapPrototypeForEach(void *, Runtime &runtime, NativeArgs args) {
222
0
  auto selfHandle = args.dyncastThis<JSMap>();
223
0
  if (LLVM_UNLIKELY(!selfHandle)) {
224
0
    return runtime.raiseTypeError(
225
0
        "Non-Map object called on Map.prototype.forEach");
226
0
  }
227
0
  auto callbackfn = args.dyncastArg<Callable>(0);
228
0
  if (LLVM_UNLIKELY(!callbackfn)) {
229
0
    return runtime.raiseTypeError(
230
0
        "callbackfn must be Callable in Map.prototype.forEach");
231
0
  }
232
0
  auto thisArg = args.getArgHandle(1);
233
0
  if (JSMap::forEach(selfHandle, runtime, callbackfn, thisArg) ==
234
0
      ExecutionStatus::EXCEPTION)
235
0
    return ExecutionStatus::EXCEPTION;
236
0
  return HermesValue::encodeUndefinedValue();
237
0
}
238
239
CallResult<HermesValue>
240
0
mapPrototypeGet(void *, Runtime &runtime, NativeArgs args) {
241
0
  auto selfHandle = args.dyncastThis<JSMap>();
242
0
  if (LLVM_UNLIKELY(!selfHandle)) {
243
0
    return runtime.raiseTypeError("Non-Map object called on Map.prototype.get");
244
0
  }
245
0
  return JSMap::getValue(selfHandle, runtime, args.getArgHandle(0));
246
0
}
247
248
CallResult<HermesValue>
249
0
mapPrototypeHas(void *, Runtime &runtime, NativeArgs args) {
250
0
  auto selfHandle = args.dyncastThis<JSMap>();
251
0
  if (LLVM_UNLIKELY(!selfHandle)) {
252
0
    return runtime.raiseTypeError("Non-Map object called on Map.prototype.has");
253
0
  }
254
0
  return HermesValue::encodeBoolValue(
255
0
      JSMap::hasKey(selfHandle, runtime, args.getArgHandle(0)));
256
0
}
257
258
CallResult<HermesValue>
259
0
mapPrototypeKeys(void *, Runtime &runtime, NativeArgs args) {
260
0
  auto selfHandle = args.dyncastThis<JSMap>();
261
0
  if (LLVM_UNLIKELY(!selfHandle)) {
262
0
    return runtime.raiseTypeError(
263
0
        "Non-Map object called on Map.prototype.keys");
264
0
  }
265
266
0
  auto iterator = runtime.makeHandle(
267
0
      JSMapIterator::create(
268
0
          runtime, Handle<JSObject>::vmcast(&runtime.mapIteratorPrototype)));
269
0
  iterator->initializeIterator(runtime, selfHandle, IterationKind::Key);
270
0
  return iterator.getHermesValue();
271
0
}
272
273
// ES12 23.1.3.9 Map.prototype.set ( key, value )
274
CallResult<HermesValue>
275
0
mapPrototypeSet(void *, Runtime &runtime, NativeArgs args) {
276
0
  auto selfHandle = args.dyncastThis<JSMap>();
277
0
  if (LLVM_UNLIKELY(!selfHandle)) {
278
0
    return runtime.raiseTypeError("Non-Map object called on Map.prototype.set");
279
0
  }
280
0
  auto keyHandle = args.getArgHandle(0);
281
  // 5. If key is -0, set key to +0.
282
  // N.B. in the case of Map, only key should be normalized but not the value.
283
0
  auto key = keyHandle->isNumber() && keyHandle->getNumber() == 0
284
0
      ? HandleRootOwner::getZeroValue()
285
0
      : keyHandle;
286
0
  JSMap::addValue(selfHandle, runtime, key, args.getArgHandle(1));
287
0
  return selfHandle.getHermesValue();
288
0
}
289
290
CallResult<HermesValue>
291
0
mapPrototypeSizeGetter(void *, Runtime &runtime, NativeArgs args) {
292
0
  auto self = dyn_vmcast<JSMap>(args.getThisArg());
293
0
  if (LLVM_UNLIKELY(!self)) {
294
0
    return runtime.raiseTypeError(
295
0
        "Non-Map object called on Map.prototype.size");
296
0
  }
297
0
  return HermesValue::encodeUntrustedNumberValue(JSMap::getSize(self, runtime));
298
0
}
299
300
CallResult<HermesValue>
301
0
mapPrototypeValues(void *, Runtime &runtime, NativeArgs args) {
302
0
  auto selfHandle = args.dyncastThis<JSMap>();
303
0
  if (LLVM_UNLIKELY(!selfHandle)) {
304
0
    return runtime.raiseTypeError(
305
0
        "Non-Map object called on Map.prototype.values");
306
0
  }
307
0
  auto iterator = runtime.makeHandle(
308
0
      JSMapIterator::create(
309
0
          runtime, Handle<JSObject>::vmcast(&runtime.mapIteratorPrototype)));
310
0
  iterator->initializeIterator(runtime, selfHandle, IterationKind::Value);
311
0
  return iterator.getHermesValue();
312
0
}
313
314
94
Handle<JSObject> createMapIteratorPrototype(Runtime &runtime) {
315
94
  auto parentHandle = runtime.makeHandle(
316
94
      JSObject::create(
317
94
          runtime, Handle<JSObject>::vmcast(&runtime.iteratorPrototype)));
318
94
  defineMethod(
319
94
      runtime,
320
94
      parentHandle,
321
94
      Predefined::getSymbolID(Predefined::next),
322
94
      nullptr,
323
94
      mapIteratorPrototypeNext,
324
94
      0);
325
326
94
  auto dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
327
94
  dpf.writable = 0;
328
94
  dpf.enumerable = 0;
329
94
  defineProperty(
330
94
      runtime,
331
94
      parentHandle,
332
94
      Predefined::getSymbolID(Predefined::SymbolToStringTag),
333
94
      runtime.getPredefinedStringHandle(Predefined::MapIterator),
334
94
      dpf);
335
336
94
  return parentHandle;
337
94
}
338
339
CallResult<HermesValue>
340
0
mapIteratorPrototypeNext(void *, Runtime &runtime, NativeArgs args) {
341
0
  auto selfHandle = args.dyncastThis<JSMapIterator>();
342
0
  if (LLVM_UNLIKELY(!selfHandle)) {
343
0
    return runtime.raiseTypeError(
344
0
        "Non-MapIterator object called on MapIterator.prototype.next");
345
0
  }
346
347
0
  auto cr = JSMapIterator::nextElement(selfHandle, runtime);
348
0
  if (LLVM_UNLIKELY(cr == ExecutionStatus::EXCEPTION)) {
349
0
    return ExecutionStatus::EXCEPTION;
350
0
  }
351
0
  return *cr;
352
0
}
353
} // namespace vm
354
} // namespace hermes