Coverage Report

Created: 2024-05-20 07:08

/src/hermes/lib/VM/JSLib/Array.cpp
Line
Count
Source (jump to first uncovered line)
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
/// ES5.1 15.4 Initialize the Array constructor.
11
//===----------------------------------------------------------------------===//
12
13
#include "JSLibInternal.h"
14
15
#include "hermes/ADT/SafeInt.h"
16
#include "hermes/VM/HandleRootOwner-inline.h"
17
#include "hermes/VM/JSLib/Sorting.h"
18
#include "hermes/VM/Operations.h"
19
#include "hermes/VM/StringBuilder.h"
20
#include "hermes/VM/StringRefUtils.h"
21
#include "hermes/VM/StringView.h"
22
23
#include "llvh/ADT/ScopeExit.h"
24
#pragma GCC diagnostic push
25
26
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
27
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
28
#endif
29
namespace hermes {
30
namespace vm {
31
32
//===----------------------------------------------------------------------===//
33
/// Array.
34
35
110
Handle<JSObject> createArrayConstructor(Runtime &runtime) {
36
110
  auto arrayPrototype = Handle<JSArray>::vmcast(&runtime.arrayPrototype);
37
38
  // Array.prototype.xxx methods.
39
110
  defineMethod(
40
110
      runtime,
41
110
      arrayPrototype,
42
110
      Predefined::getSymbolID(Predefined::toString),
43
110
      nullptr,
44
110
      arrayPrototypeToString,
45
110
      0);
46
110
  defineMethod(
47
110
      runtime,
48
110
      arrayPrototype,
49
110
      Predefined::getSymbolID(Predefined::toLocaleString),
50
110
      nullptr,
51
110
      arrayPrototypeToLocaleString,
52
110
      0);
53
110
  defineMethod(
54
110
      runtime,
55
110
      arrayPrototype,
56
110
      Predefined::getSymbolID(Predefined::at),
57
110
      nullptr,
58
110
      arrayPrototypeAt,
59
110
      1);
60
110
  defineMethod(
61
110
      runtime,
62
110
      arrayPrototype,
63
110
      Predefined::getSymbolID(Predefined::concat),
64
110
      nullptr,
65
110
      arrayPrototypeConcat,
66
110
      1);
67
110
  defineMethod(
68
110
      runtime,
69
110
      arrayPrototype,
70
110
      Predefined::getSymbolID(Predefined::join),
71
110
      nullptr,
72
110
      arrayPrototypeJoin,
73
110
      1);
74
110
  defineMethod(
75
110
      runtime,
76
110
      arrayPrototype,
77
110
      Predefined::getSymbolID(Predefined::push),
78
110
      nullptr,
79
110
      arrayPrototypePush,
80
110
      1);
81
110
  defineMethod(
82
110
      runtime,
83
110
      arrayPrototype,
84
110
      Predefined::getSymbolID(Predefined::sort),
85
110
      nullptr,
86
110
      arrayPrototypeSort,
87
110
      1);
88
110
  defineMethod(
89
110
      runtime,
90
110
      arrayPrototype,
91
110
      Predefined::getSymbolID(Predefined::forEach),
92
110
      nullptr,
93
110
      arrayPrototypeForEach,
94
110
      1);
95
110
  defineMethod(
96
110
      runtime,
97
110
      arrayPrototype,
98
110
      Predefined::getSymbolID(Predefined::flat),
99
110
      nullptr,
100
110
      arrayPrototypeFlat,
101
110
      0);
102
110
  defineMethod(
103
110
      runtime,
104
110
      arrayPrototype,
105
110
      Predefined::getSymbolID(Predefined::flatMap),
106
110
      nullptr,
107
110
      arrayPrototypeFlatMap,
108
110
      1);
109
110
110
  defineMethod(
111
110
      runtime,
112
110
      arrayPrototype,
113
110
      Predefined::getSymbolID(Predefined::keys),
114
110
      (void *)IterationKind::Key,
115
110
      arrayPrototypeIterator,
116
110
      0);
117
110
  defineMethod(
118
110
      runtime,
119
110
      arrayPrototype,
120
110
      Predefined::getSymbolID(Predefined::values),
121
110
      (void *)IterationKind::Value,
122
110
      arrayPrototypeIterator,
123
110
      0);
124
110
  defineMethod(
125
110
      runtime,
126
110
      arrayPrototype,
127
110
      Predefined::getSymbolID(Predefined::entries),
128
110
      (void *)IterationKind::Entry,
129
110
      arrayPrototypeIterator,
130
110
      0);
131
110
  defineMethod(
132
110
      runtime,
133
110
      arrayPrototype,
134
110
      Predefined::getSymbolID(Predefined::toReversed),
135
110
      nullptr,
136
110
      arrayPrototypeToReversed,
137
110
      0);
138
110
  defineMethod(
139
110
      runtime,
140
110
      arrayPrototype,
141
110
      Predefined::getSymbolID(Predefined::toSpliced),
142
110
      nullptr,
143
110
      arrayPrototypeToSpliced,
144
110
      2);
145
110
  defineMethod(
146
110
      runtime,
147
110
      arrayPrototype,
148
110
      Predefined::getSymbolID(Predefined::with),
149
110
      nullptr,
150
110
      arrayPrototypeWith,
151
110
      2);
152
153
110
  auto propValue = runtime.ignoreAllocationFailure(JSObject::getNamed_RJS(
154
110
      arrayPrototype, runtime, Predefined::getSymbolID(Predefined::values)));
155
110
  runtime.arrayPrototypeValues = std::move(propValue);
156
157
110
  DefinePropertyFlags dpf = DefinePropertyFlags::getNewNonEnumerableFlags();
158
159
110
  runtime.ignoreAllocationFailure(JSObject::defineOwnProperty(
160
110
      arrayPrototype,
161
110
      runtime,
162
110
      Predefined::getSymbolID(Predefined::SymbolIterator),
163
110
      dpf,
164
110
      Handle<>(&runtime.arrayPrototypeValues)));
165
166
110
  auto cons = defineSystemConstructor<JSArray>(
167
110
      runtime,
168
110
      Predefined::getSymbolID(Predefined::Array),
169
110
      arrayConstructor,
170
110
      arrayPrototype,
171
110
      1,
172
110
      CellKind::JSArrayKind);
173
174
110
  defineMethod(
175
110
      runtime,
176
110
      cons,
177
110
      Predefined::getSymbolID(Predefined::isArray),
178
110
      nullptr,
179
110
      arrayIsArray,
180
110
      1);
181
182
110
  defineMethod(
183
110
      runtime,
184
110
      arrayPrototype,
185
110
      Predefined::getSymbolID(Predefined::slice),
186
110
      nullptr,
187
110
      arrayPrototypeSlice,
188
110
      2);
189
110
  defineMethod(
190
110
      runtime,
191
110
      arrayPrototype,
192
110
      Predefined::getSymbolID(Predefined::splice),
193
110
      nullptr,
194
110
      arrayPrototypeSplice,
195
110
      2);
196
110
  defineMethod(
197
110
      runtime,
198
110
      arrayPrototype,
199
110
      Predefined::getSymbolID(Predefined::copyWithin),
200
110
      nullptr,
201
110
      arrayPrototypeCopyWithin,
202
110
      2);
203
110
  defineMethod(
204
110
      runtime,
205
110
      arrayPrototype,
206
110
      Predefined::getSymbolID(Predefined::pop),
207
110
      nullptr,
208
110
      arrayPrototypePop,
209
110
      0);
210
110
  defineMethod(
211
110
      runtime,
212
110
      arrayPrototype,
213
110
      Predefined::getSymbolID(Predefined::shift),
214
110
      nullptr,
215
110
      arrayPrototypeShift,
216
110
      0);
217
110
  defineMethod(
218
110
      runtime,
219
110
      arrayPrototype,
220
110
      Predefined::getSymbolID(Predefined::unshift),
221
110
      nullptr,
222
110
      arrayPrototypeUnshift,
223
110
      1);
224
110
  defineMethod(
225
110
      runtime,
226
110
      arrayPrototype,
227
110
      Predefined::getSymbolID(Predefined::indexOf),
228
110
      nullptr,
229
110
      arrayPrototypeIndexOf,
230
110
      1);
231
110
  defineMethod(
232
110
      runtime,
233
110
      arrayPrototype,
234
110
      Predefined::getSymbolID(Predefined::lastIndexOf),
235
110
      nullptr,
236
110
      arrayPrototypeLastIndexOf,
237
110
      1);
238
110
  defineMethod(
239
110
      runtime,
240
110
      arrayPrototype,
241
110
      Predefined::getSymbolID(Predefined::every),
242
110
      nullptr,
243
110
      arrayPrototypeEvery,
244
110
      1);
245
110
  defineMethod(
246
110
      runtime,
247
110
      arrayPrototype,
248
110
      Predefined::getSymbolID(Predefined::some),
249
110
      nullptr,
250
110
      arrayPrototypeSome,
251
110
      1);
252
110
  defineMethod(
253
110
      runtime,
254
110
      arrayPrototype,
255
110
      Predefined::getSymbolID(Predefined::map),
256
110
      nullptr,
257
110
      arrayPrototypeMap,
258
110
      1);
259
110
  defineMethod(
260
110
      runtime,
261
110
      arrayPrototype,
262
110
      Predefined::getSymbolID(Predefined::filter),
263
110
      nullptr,
264
110
      arrayPrototypeFilter,
265
110
      1);
266
110
  defineMethod(
267
110
      runtime,
268
110
      arrayPrototype,
269
110
      Predefined::getSymbolID(Predefined::fill),
270
110
      nullptr,
271
110
      arrayPrototypeFill,
272
110
      1);
273
110
  defineMethod(
274
110
      runtime,
275
110
      arrayPrototype,
276
110
      Predefined::getSymbolID(Predefined::find),
277
110
      nullptr,
278
110
      arrayPrototypeFind,
279
110
      1);
280
110
  defineMethod(
281
110
      runtime,
282
110
      arrayPrototype,
283
110
      Predefined::getSymbolID(Predefined::findIndex),
284
      // Pass a non-null pointer here to indicate we're finding the index.
285
110
      (void *)true,
286
110
      arrayPrototypeFind,
287
110
      1);
288
110
  defineMethod(
289
110
      runtime,
290
110
      arrayPrototype,
291
110
      Predefined::getSymbolID(Predefined::findLast),
292
110
      nullptr,
293
110
      arrayPrototypeFindLast,
294
110
      1);
295
110
  defineMethod(
296
110
      runtime,
297
110
      arrayPrototype,
298
110
      Predefined::getSymbolID(Predefined::findLastIndex),
299
      // Pass a non-null pointer here to indicate we're finding the index.
300
110
      (void *)true,
301
110
      arrayPrototypeFindLast,
302
110
      1);
303
110
  defineMethod(
304
110
      runtime,
305
110
      arrayPrototype,
306
110
      Predefined::getSymbolID(Predefined::reduce),
307
110
      nullptr,
308
110
      arrayPrototypeReduce,
309
110
      1);
310
110
  defineMethod(
311
110
      runtime,
312
110
      arrayPrototype,
313
110
      Predefined::getSymbolID(Predefined::reduceRight),
314
110
      nullptr,
315
110
      arrayPrototypeReduceRight,
316
110
      1);
317
110
  defineMethod(
318
110
      runtime,
319
110
      arrayPrototype,
320
110
      Predefined::getSymbolID(Predefined::reverse),
321
110
      nullptr,
322
110
      arrayPrototypeReverse,
323
110
      0);
324
110
  defineMethod(
325
110
      runtime,
326
110
      arrayPrototype,
327
110
      Predefined::getSymbolID(Predefined::includes),
328
110
      nullptr,
329
110
      arrayPrototypeIncludes,
330
110
      1);
331
332
110
  defineMethod(
333
110
      runtime,
334
110
      cons,
335
110
      Predefined::getSymbolID(Predefined::of),
336
110
      nullptr,
337
110
      arrayOf,
338
110
      0);
339
110
  defineMethod(
340
110
      runtime,
341
110
      cons,
342
110
      Predefined::getSymbolID(Predefined::from),
343
110
      nullptr,
344
110
      arrayFrom,
345
110
      1);
346
347
110
  return cons;
348
110
}
349
350
CallResult<HermesValue>
351
0
arrayConstructor(void *, Runtime &runtime, NativeArgs args) {
352
0
  MutableHandle<JSArray> selfHandle{runtime};
353
354
  // If constructor, use the allocated object, otherwise allocate a new one.
355
  // Everything else is the same after that.
356
0
  if (args.isConstructorCall())
357
0
    selfHandle = vmcast<JSArray>(args.getThisArg());
358
0
  else {
359
0
    auto arrRes = JSArray::create(runtime, 0, 0);
360
0
    if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
361
0
      return ExecutionStatus::EXCEPTION;
362
0
    }
363
0
    selfHandle = arrRes->get();
364
0
  }
365
366
  // Possibility 1: new Array(number)
367
0
  if (args.getArgCount() == 1 && args.getArg(0).isNumber()) {
368
0
    double number = args.getArg(0).getNumber();
369
0
    uint32_t len = truncateToUInt32(number);
370
0
    if (len != number) {
371
0
      return runtime.raiseRangeError("invalid array length");
372
0
    }
373
374
0
    auto st = JSArray::setLengthProperty(selfHandle, runtime, len);
375
0
    (void)st;
376
0
    assert(
377
0
        st != ExecutionStatus::EXCEPTION && *st &&
378
0
        "Cannot set length of a new array");
379
380
0
    return selfHandle.getHermesValue();
381
0
  }
382
383
  // Possibility 2: new Array(elements...)
384
0
  uint32_t len = args.getArgCount();
385
386
  // Resize the array.
387
0
  auto st = JSArray::setLengthProperty(selfHandle, runtime, len);
388
0
  (void)st;
389
0
  assert(
390
0
      st != ExecutionStatus::EXCEPTION && *st &&
391
0
      "Cannot set length of a new array");
392
393
  // Initialize the elements.
394
0
  uint32_t index = 0;
395
0
  GCScopeMarkerRAII marker(runtime);
396
0
  for (Handle<> arg : args.handles()) {
397
0
    JSArray::setElementAt(selfHandle, runtime, index++, arg);
398
0
    marker.flush();
399
0
  }
400
401
0
  return selfHandle.getHermesValue();
402
0
}
403
404
CallResult<HermesValue>
405
0
arrayIsArray(void *, Runtime &runtime, NativeArgs args) {
406
0
  CallResult<bool> res = isArray(runtime, dyn_vmcast<JSObject>(args.getArg(0)));
407
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
408
0
    return ExecutionStatus::EXCEPTION;
409
0
  }
410
0
  return HermesValue::encodeBoolValue(*res);
411
0
}
412
413
/// ES5.1 15.4.4.5.
414
CallResult<HermesValue>
415
18
arrayPrototypeToString(void *, Runtime &runtime, NativeArgs args) {
416
18
  auto objRes = toObject(runtime, args.getThisHandle());
417
18
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
418
0
    return ExecutionStatus::EXCEPTION;
419
0
  }
420
18
  auto array = runtime.makeHandle<JSObject>(objRes.getValue());
421
422
18
  auto propRes = JSObject::getNamed_RJS(
423
18
      array, runtime, Predefined::getSymbolID(Predefined::join));
424
18
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
425
0
    return ExecutionStatus::EXCEPTION;
426
0
  }
427
18
  auto func =
428
18
      Handle<Callable>::dyn_vmcast(runtime.makeHandle(std::move(*propRes)));
429
430
18
  if (!func) {
431
    // If not callable, set func to be Object.prototype.toString.
432
0
    return directObjectPrototypeToString(runtime, array);
433
0
  }
434
435
18
  return Callable::executeCall0(func, runtime, array).toCallResultHermesValue();
436
18
}
437
438
CallResult<HermesValue>
439
0
arrayPrototypeToLocaleString(void *, Runtime &runtime, NativeArgs args) {
440
0
  GCScope gcScope{runtime};
441
0
  auto objRes = toObject(runtime, args.getThisHandle());
442
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
443
0
    return ExecutionStatus::EXCEPTION;
444
0
  }
445
0
  auto array = runtime.makeHandle<JSObject>(objRes.getValue());
446
447
0
  auto emptyString = runtime.getPredefinedStringHandle(Predefined::emptyString);
448
449
0
  if (runtime.insertVisitedObject(*array))
450
0
    return emptyString.getHermesValue();
451
0
  auto cycleScope =
452
0
      llvh::make_scope_exit([&] { runtime.removeVisitedObject(*array); });
453
454
0
  auto propRes = JSObject::getNamed_RJS(
455
0
      array, runtime, Predefined::getSymbolID(Predefined::length));
456
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
457
0
    return ExecutionStatus::EXCEPTION;
458
0
  }
459
0
  auto intRes = toUInt32_RJS(runtime, runtime.makeHandle(std::move(*propRes)));
460
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
461
0
    return ExecutionStatus::EXCEPTION;
462
0
  }
463
0
  uint32_t len = intRes->getNumber();
464
465
  // TODO: Get a list-separator String for the host environment's locale.
466
  // Use a comma as a separator for now, as JSC does.
467
0
  const char16_t separator = u',';
468
469
  // Final size of the result string. Initialize to account for the separators.
470
0
  SafeUInt32 size(len - 1);
471
472
0
  if (len == 0) {
473
0
    return emptyString.getHermesValue();
474
0
  }
475
476
  // Array to store each of the strings of the elements.
477
0
  auto arrRes = JSArray::create(runtime, len, len);
478
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
479
0
    return ExecutionStatus::EXCEPTION;
480
0
  }
481
0
  auto strings = *arrRes;
482
483
  // Index into the array.
484
0
  MutableHandle<> i{runtime, HermesValue::encodeTrustedNumberValue(0)};
485
486
0
  auto marker = gcScope.createMarker();
487
0
  while (i->getNumber() < len) {
488
0
    gcScope.flushToMarker(marker);
489
0
    if (LLVM_UNLIKELY(
490
0
            (propRes = JSObject::getComputed_RJS(array, runtime, i)) ==
491
0
            ExecutionStatus::EXCEPTION)) {
492
0
      return ExecutionStatus::EXCEPTION;
493
0
    }
494
0
    auto E = runtime.makeHandle(std::move(*propRes));
495
0
    if (E->isUndefined() || E->isNull()) {
496
      // Empty string for undefined or null element. No need to add to size.
497
0
      JSArray::setElementAt(strings, runtime, i->getNumber(), emptyString);
498
0
    } else {
499
0
      if (LLVM_UNLIKELY(
500
0
              (objRes = toObject(runtime, E)) == ExecutionStatus::EXCEPTION)) {
501
0
        return ExecutionStatus::EXCEPTION;
502
0
      }
503
0
      auto elementObj = runtime.makeHandle<JSObject>(objRes.getValue());
504
505
      // Retrieve the toLocaleString function.
506
0
      if (LLVM_UNLIKELY(
507
0
              (propRes = JSObject::getNamed_RJS(
508
0
                   elementObj,
509
0
                   runtime,
510
0
                   Predefined::getSymbolID(Predefined::toLocaleString))) ==
511
0
              ExecutionStatus::EXCEPTION)) {
512
0
        return ExecutionStatus::EXCEPTION;
513
0
      }
514
0
      if (auto func = Handle<Callable>::dyn_vmcast(
515
0
              runtime.makeHandle(std::move(*propRes)))) {
516
        // If ECMA 402 is implemented, it provides a superseding
517
        // definition of Array.prototype.toLocaleString.  The only
518
        // difference between these two definitions is that in ECMA
519
        // 402, two arguments (locales and options), if provided, are
520
        // passed on from this function to the element's
521
        // "toLocaleString" method.
522
0
        auto callRes =
523
#ifdef HERMES_ENABLE_INTL
524
            Callable::executeCall2(
525
                func, runtime, elementObj, args.getArg(0), args.getArg(1));
526
#else
527
0
            Callable::executeCall0(func, runtime, elementObj);
528
0
#endif
529
0
        if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
530
0
          return ExecutionStatus::EXCEPTION;
531
0
        }
532
0
        auto strRes =
533
0
            toString_RJS(runtime, runtime.makeHandle(std::move(*callRes)));
534
0
        if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
535
0
          return ExecutionStatus::EXCEPTION;
536
0
        }
537
0
        auto elementStr = runtime.makeHandle(std::move(*strRes));
538
0
        uint32_t strLength = elementStr->getStringLength();
539
        // Throw RangeError on overflow.
540
0
        size.add(strLength);
541
0
        if (LLVM_UNLIKELY(size.isOverflowed())) {
542
0
          return runtime.raiseRangeError(
543
0
              "resulting string length exceeds limit");
544
0
        }
545
0
        JSArray::setElementAt(strings, runtime, i->getNumber(), elementStr);
546
0
      } else {
547
0
        return runtime.raiseTypeError("toLocaleString() not callable");
548
0
      }
549
0
    }
550
0
    i = HermesValue::encodeTrustedNumberValue(i->getNumber() + 1);
551
0
  }
552
553
  // Create and then populate the result string.
554
0
  auto builder = StringBuilder::createStringBuilder(runtime, size);
555
0
  if (builder == ExecutionStatus::EXCEPTION) {
556
0
    return ExecutionStatus::EXCEPTION;
557
0
  }
558
0
  MutableHandle<StringPrimitive> element{runtime};
559
0
  element = strings->at(runtime, 0).getString(runtime);
560
0
  builder->appendStringPrim(element);
561
0
  for (uint32_t j = 1; j < len; ++j) {
562
    // Every element after the first needs a separator before it.
563
0
    builder->appendCharacter(separator);
564
0
    element = strings->at(runtime, j).getString(runtime);
565
0
    builder->appendStringPrim(element);
566
0
  }
567
0
  return HermesValue::encodeStringValue(*builder->getStringPrimitive());
568
0
}
569
570
static inline CallResult<uint64_t>
571
0
lengthOfArrayLike(Runtime &runtime, Handle<JSObject> O, Handle<JSArray> jsArr) {
572
0
  if (LLVM_LIKELY(jsArr)) {
573
    // Fast path for getting the length.
574
0
    return JSArray::getLength(jsArr.get(), runtime);
575
0
  }
576
  // Slow path
577
0
  CallResult<PseudoHandle<>> propRes = JSObject::getNamed_RJS(
578
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
579
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
580
0
    return ExecutionStatus::EXCEPTION;
581
0
  }
582
0
  auto lenRes = toLength(runtime, runtime.makeHandle(std::move(*propRes)));
583
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
584
0
    return ExecutionStatus::EXCEPTION;
585
0
  }
586
587
0
  return lenRes->getNumber();
588
0
}
589
590
// 23.1.3.1
591
CallResult<HermesValue>
592
0
arrayPrototypeAt(void *, Runtime &runtime, NativeArgs args) {
593
0
  GCScope gcScope(runtime);
594
  // 1. Let O be ? ToObject(this value).
595
0
  auto objRes = toObject(runtime, args.getThisHandle());
596
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
597
0
    return ExecutionStatus::EXCEPTION;
598
0
  }
599
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
600
601
  // 2. Let len be ? LengthOfArrayLike(O).
602
0
  Handle<JSArray> jsArr = Handle<JSArray>::dyn_vmcast(O);
603
0
  auto lenRes = lengthOfArrayLike(runtime, O, jsArr);
604
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
605
0
    return ExecutionStatus::EXCEPTION;
606
0
  }
607
0
  auto len = lenRes.getValue();
608
609
  // 3. Let relativeIndex be ? ToIntegerOrInfinity(index).
610
0
  auto idx = args.getArgHandle(0);
611
0
  auto relativeIndexRes = toIntegerOrInfinity(runtime, idx);
612
0
  if (relativeIndexRes == ExecutionStatus::EXCEPTION) {
613
0
    return ExecutionStatus::EXCEPTION;
614
0
  }
615
0
  const double relativeIndex = relativeIndexRes->getNumber();
616
617
0
  double k;
618
  // 4. If relativeIndex ≥ 0, then
619
0
  if (relativeIndex >= 0) {
620
    // a. Let k be relativeIndex.
621
0
    k = relativeIndex;
622
0
  } else {
623
    // 5. Else,
624
    // a. Let k be len + relativeIndex.
625
0
    k = len + relativeIndex;
626
0
  }
627
628
  // 6. If k < 0 or k ≥ len, return undefined.
629
0
  if (k < 0 || k >= len) {
630
0
    return HermesValue::encodeUndefinedValue();
631
0
  }
632
633
  // 7. Return ? Get(O, ! ToString(𝔽(k))).
634
0
  if (LLVM_LIKELY(jsArr)) {
635
0
    const SmallHermesValue elm = jsArr->at(runtime, k);
636
    // If the element is not empty, we can return it directly here. Otherwise,
637
    // we must proceed to the slow path.
638
0
    if (!elm.isEmpty()) {
639
0
      return elm.unboxToHV(runtime);
640
0
    }
641
0
  }
642
0
  CallResult<PseudoHandle<>> propRes = JSObject::getComputed_RJS(
643
0
      O, runtime, runtime.makeHandle(HermesValue::encodeTrustedNumberValue(k)));
644
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
645
0
    return ExecutionStatus::EXCEPTION;
646
0
  }
647
0
  return propRes->getHermesValue();
648
0
}
649
650
CallResult<HermesValue>
651
0
arrayPrototypeConcat(void *, Runtime &runtime, NativeArgs args) {
652
0
  GCScope gcScope(runtime);
653
0
  auto objRes = toObject(runtime, args.getThisHandle());
654
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
655
0
    return ExecutionStatus::EXCEPTION;
656
0
  }
657
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
658
659
  // Need a signed type here to account for uint32 and -1.
660
0
  int64_t argCount = args.getArgCount();
661
662
  // Precompute the final size of the array so it can be preallocated.
663
  // Note this is necessarily an estimate because an accessor on one array
664
  // may change the length of subsequent arrays.
665
0
  SafeUInt32 finalSizeEstimate{0};
666
0
  if (JSArray *arr = dyn_vmcast<JSArray>(O.get())) {
667
0
    finalSizeEstimate.add(JSArray::getLength(arr, runtime));
668
0
  } else {
669
0
    finalSizeEstimate.add(1);
670
0
  }
671
0
  for (int64_t i = 0; i < argCount; ++i) {
672
0
    if (JSArray *arr = dyn_vmcast<JSArray>(args.getArg(i))) {
673
0
      finalSizeEstimate.add(JSArray::getLength(arr, runtime));
674
0
    } else {
675
0
      finalSizeEstimate.add(1);
676
0
    }
677
0
  }
678
0
  if (finalSizeEstimate.isOverflowed()) {
679
0
    return runtime.raiseTypeError("Array.prototype.concat result out of space");
680
0
  }
681
682
  // Resultant array.
683
0
  auto arrRes =
684
0
      JSArray::create(runtime, *finalSizeEstimate, *finalSizeEstimate);
685
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
686
0
    return ExecutionStatus::EXCEPTION;
687
0
  }
688
0
  auto A = *arrRes;
689
690
  // Index to insert into A.
691
0
  uint64_t n = 0;
692
693
  // Temporary handle for an object.
694
0
  MutableHandle<JSObject> objHandle{runtime};
695
  // Temporary handle for an array.
696
0
  MutableHandle<JSArray> arrHandle{runtime};
697
  // Index to read from in the array that's being concatenated.
698
0
  MutableHandle<> kHandle{runtime};
699
  // Index to put into the resultant array.
700
0
  MutableHandle<> nHandle{runtime};
701
  // Temporary handle to use when holding intermediate elements.
702
0
  MutableHandle<> tmpHandle{runtime};
703
  // Used to find the object in the prototype chain that has index as property.
704
0
  MutableHandle<JSObject> propObj{runtime};
705
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
706
0
  auto marker = gcScope.createMarker();
707
0
  ComputedPropertyDescriptor desc;
708
709
  // Loop first through the "this" value and then through the arguments.
710
  // If i == -1, use the "this" value, else use the ith argument.
711
0
  tmpHandle = O.getHermesValue();
712
0
  for (int64_t i = -1; i < argCount; ++i, tmpHandle = args.getArg(i)) {
713
0
    CallResult<bool> spreadable = isConcatSpreadable(runtime, tmpHandle);
714
0
    if (LLVM_UNLIKELY(spreadable == ExecutionStatus::EXCEPTION)) {
715
0
      return ExecutionStatus::EXCEPTION;
716
0
    }
717
0
    if (*spreadable) {
718
      // 7.d. If spreadable is true, then
719
0
      objHandle = vmcast<JSObject>(*tmpHandle);
720
0
      arrHandle = dyn_vmcast<JSArray>(*tmpHandle);
721
722
0
      uint64_t len;
723
0
      if (LLVM_LIKELY(arrHandle)) {
724
        // Fast path: E is an array.
725
0
        len = JSArray::getLength(*arrHandle, runtime);
726
0
      } else {
727
0
        CallResult<PseudoHandle<>> propRes = JSObject::getNamed_RJS(
728
0
            objHandle, runtime, Predefined::getSymbolID(Predefined::length));
729
0
        if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
730
0
          return ExecutionStatus::EXCEPTION;
731
0
        }
732
0
        tmpHandle = std::move(*propRes);
733
0
        auto lengthRes = toLength(runtime, tmpHandle);
734
0
        if (LLVM_UNLIKELY(lengthRes == ExecutionStatus::EXCEPTION)) {
735
0
          return ExecutionStatus::EXCEPTION;
736
0
        }
737
0
        len = lengthRes->getNumberAs<uint64_t>();
738
0
      }
739
740
      // 5.c.iii. If n + len > 2^53 - 1, throw a TypeError exception
741
0
      if (LLVM_UNLIKELY(n + len > ((uint64_t)1 << 53) - 1)) {
742
0
        return runtime.raiseTypeError(
743
0
            "Array.prototype.concat result out of space");
744
0
      }
745
746
      // We know we are going to set elements in the range [n, n+len),
747
      // regardless of any changes to 'arrHandle' (see ES5.1 15.4.4.4). Ensure
748
      // we have capacity.
749
0
      if (LLVM_UNLIKELY(n + len > A->getEndIndex()) &&
750
0
          LLVM_LIKELY(n + len < UINT32_MAX)) {
751
        // Only set the endIndex if it's going to be a valid length.
752
0
        if (LLVM_UNLIKELY(
753
0
                A->setStorageEndIndex(A, runtime, n + len) ==
754
0
                ExecutionStatus::EXCEPTION)) {
755
0
          return ExecutionStatus::EXCEPTION;
756
0
        }
757
0
      }
758
759
      // Note that we must increase n every iteration even if nothing was
760
      // appended to the result array.
761
      // 5.c.iv. Repeat, while k < len
762
0
      for (uint64_t k = 0; k < len; ++k, ++n) {
763
0
        SmallHermesValue subElement = LLVM_LIKELY(arrHandle)
764
0
            ? arrHandle->at(runtime, k)
765
0
            : SmallHermesValue::encodeEmptyValue();
766
0
        if (LLVM_LIKELY(!subElement.isEmpty()) &&
767
0
            LLVM_LIKELY(n < A->getEndIndex())) {
768
          // Fast path: quickly set element without making any extra calls.
769
          // Cast is safe because A->getEndIndex must be in uint32_t range.
770
0
          JSArray::unsafeSetExistingElementAt(
771
0
              A.get(), runtime, static_cast<uint32_t>(n), subElement);
772
0
        } else {
773
          // Slow path fallback if there's an empty slot in arr.
774
          // We have to use getComputedPrimitiveDescriptor because the property
775
          // may exist anywhere in the prototype chain.
776
0
          kHandle = HermesValue::encodeTrustedNumberValue(k);
777
0
          JSObject::getComputedPrimitiveDescriptor(
778
0
              objHandle, runtime, kHandle, propObj, tmpPropNameStorage, desc);
779
0
          CallResult<PseudoHandle<>> propRes =
780
0
              JSObject::getComputedPropertyValue_RJS(
781
0
                  objHandle,
782
0
                  runtime,
783
0
                  propObj,
784
0
                  tmpPropNameStorage,
785
0
                  desc,
786
0
                  kHandle);
787
0
          if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
788
0
            return ExecutionStatus::EXCEPTION;
789
0
          }
790
0
          if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
791
0
            tmpHandle = std::move(*propRes);
792
0
            nHandle = HermesValue::encodeTrustedNumberValue(n);
793
0
            if (LLVM_UNLIKELY(
794
0
                    JSArray::defineOwnComputedPrimitive(
795
0
                        A,
796
0
                        runtime,
797
0
                        nHandle,
798
0
                        DefinePropertyFlags::getDefaultNewPropertyFlags(),
799
0
                        tmpHandle) == ExecutionStatus::EXCEPTION)) {
800
0
              return ExecutionStatus::EXCEPTION;
801
0
            }
802
0
          }
803
0
          gcScope.flushToMarker(marker);
804
0
        }
805
0
      }
806
0
      gcScope.flushToMarker(marker);
807
0
    } else {
808
      // 5.d.i. NOTE: E is added as a single item rather than spread.
809
      // 5.d.ii. If n >= 2**53 - 1, throw a TypeError exception.
810
0
      if (LLVM_UNLIKELY(n >= ((uint64_t)1 << 53) - 1)) {
811
0
        return runtime.raiseTypeError(
812
0
            "Array.prototype.concat result out of space");
813
0
      }
814
      // Otherwise, just put the value into the next slot.
815
0
      if (LLVM_LIKELY(n < UINT32_MAX)) {
816
0
        JSArray::setElementAt(A, runtime, n, tmpHandle);
817
0
      } else {
818
0
        nHandle = HermesValue::encodeTrustedNumberValue(n);
819
0
        auto cr = valueToSymbolID(runtime, nHandle);
820
0
        if (LLVM_UNLIKELY(cr == ExecutionStatus::EXCEPTION)) {
821
0
          return ExecutionStatus::EXCEPTION;
822
0
        }
823
0
        if (LLVM_UNLIKELY(
824
0
                JSArray::defineOwnProperty(
825
0
                    A,
826
0
                    runtime,
827
0
                    **cr,
828
0
                    DefinePropertyFlags::getDefaultNewPropertyFlags(),
829
0
                    tmpHandle) == ExecutionStatus::EXCEPTION)) {
830
0
          return ExecutionStatus::EXCEPTION;
831
0
        }
832
0
      }
833
0
      gcScope.flushToMarker(marker);
834
0
      ++n;
835
0
    }
836
0
  }
837
  // Update the array's length. We never expect this to fail since we just
838
  // created the array.
839
0
  if (n > UINT32_MAX) {
840
0
    return runtime.raiseRangeError("invalid array length");
841
0
  }
842
0
  auto res = JSArray::setLengthProperty(A, runtime, static_cast<uint32_t>(n));
843
0
  assert(
844
0
      res == ExecutionStatus::RETURNED &&
845
0
      "Setting length of new array should never fail");
846
0
  (void)res;
847
0
  return A.getHermesValue();
848
0
}
849
850
/// ES5.1 15.4.4.5.
851
CallResult<HermesValue>
852
17
arrayPrototypeJoin(void *, Runtime &runtime, NativeArgs args) {
853
17
  GCScope gcScope(runtime);
854
17
  auto objRes = toObject(runtime, args.getThisHandle());
855
17
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
856
0
    return ExecutionStatus::EXCEPTION;
857
0
  }
858
17
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
859
860
17
  auto emptyString = runtime.getPredefinedStringHandle(Predefined::emptyString);
861
862
17
  if (runtime.insertVisitedObject(*O))
863
0
    return emptyString.getHermesValue();
864
17
  auto cycleScope =
865
17
      llvh::make_scope_exit([&] { runtime.removeVisitedObject(*O); });
866
867
17
  auto propRes = JSObject::getNamed_RJS(
868
17
      O, runtime, Predefined::getSymbolID(Predefined::length));
869
17
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
870
0
    return ExecutionStatus::EXCEPTION;
871
0
  }
872
17
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
873
17
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
874
0
    return ExecutionStatus::EXCEPTION;
875
0
  }
876
17
  uint64_t len = *intRes;
877
878
  // Use comma for separator if the first argument is undefined.
879
17
  auto separator = args.getArg(0).isUndefined()
880
17
      ? runtime.makeHandle(HermesValue::encodeStringValue(
881
17
            runtime.getPredefinedString(Predefined::comma)))
882
17
      : args.getArgHandle(0);
883
17
  auto strRes = toString_RJS(runtime, separator);
884
17
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
885
0
    return ExecutionStatus::EXCEPTION;
886
0
  }
887
17
  auto sep = runtime.makeHandle(std::move(*strRes));
888
889
17
  if (len == 0) {
890
0
    return HermesValue::encodeStringValue(
891
0
        runtime.getPredefinedString(Predefined::emptyString));
892
0
  }
893
894
  // Track the size of the resultant string. Use a 64-bit value to detect
895
  // overflow.
896
17
  SafeUInt32 size;
897
898
  // Storage for the strings for each element.
899
17
  if (LLVM_UNLIKELY(len > JSArray::StorageType::maxElements())) {
900
0
    return runtime.raiseRangeError("Out of memory for array elements.");
901
0
  }
902
17
  auto arrRes = JSArray::create(runtime, len, 0);
903
17
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
904
0
    return ExecutionStatus::EXCEPTION;
905
0
  }
906
17
  auto strings = *arrRes;
907
908
  // Call toString on all the elements of the array.
909
17
  for (MutableHandle<> i{runtime, HermesValue::encodeTrustedNumberValue(0)};
910
98.6k
       i->getNumber() < len;
911
98.6k
       i = HermesValue::encodeTrustedNumberValue(i->getNumber() + 1)) {
912
    // Add the size of the separator, except the first time.
913
98.6k
    if (i->getNumberAs<uint32_t>())
914
98.6k
      size.add(sep->getStringLength());
915
916
98.6k
    GCScope gcScope2(runtime);
917
98.6k
    if (LLVM_UNLIKELY(
918
98.6k
            (propRes = JSObject::getComputed_RJS(O, runtime, i)) ==
919
98.6k
            ExecutionStatus::EXCEPTION)) {
920
0
      return ExecutionStatus::EXCEPTION;
921
0
    }
922
923
98.6k
    auto elem = runtime.makeHandle(std::move(*propRes));
924
925
98.6k
    if (elem->isUndefined() || elem->isNull()) {
926
3
      JSArray::setElementAt(strings, runtime, i->getNumber(), emptyString);
927
98.6k
    } else {
928
      // Otherwise, call toString_RJS() and push the result, incrementing size.
929
98.6k
      auto strRes = toString_RJS(runtime, elem);
930
98.6k
      if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
931
0
        return ExecutionStatus::EXCEPTION;
932
0
      }
933
98.6k
      auto S = runtime.makeHandle(std::move(*strRes));
934
98.6k
      size.add(S->getStringLength());
935
98.6k
      JSArray::setElementAt(strings, runtime, i->getNumber(), S);
936
98.6k
    }
937
938
    // Check for string overflow on every iteration to create the illusion that
939
    // we are appending to the string. Also, prevent uint32_t overflow.
940
98.6k
    if (size.isOverflowed()) {
941
0
      return runtime.raiseRangeError("String is too long");
942
0
    }
943
98.6k
  }
944
945
  // Allocate the complete result.
946
17
  auto builder = StringBuilder::createStringBuilder(runtime, size);
947
17
  if (builder == ExecutionStatus::EXCEPTION) {
948
0
    return ExecutionStatus::EXCEPTION;
949
0
  }
950
17
  MutableHandle<StringPrimitive> element{runtime};
951
17
  element = strings->at(runtime, 0).getString(runtime);
952
17
  builder->appendStringPrim(element);
953
98.6k
  for (size_t i = 1; i < len; ++i) {
954
98.6k
    builder->appendStringPrim(sep);
955
98.6k
    element = strings->at(runtime, i).getString(runtime);
956
98.6k
    builder->appendStringPrim(element);
957
98.6k
  }
958
17
  return HermesValue::encodeStringValue(*builder->getStringPrimitive());
959
17
}
960
961
/// ES9.0 22.1.3.18.
962
CallResult<HermesValue>
963
0
arrayPrototypePush(void *, Runtime &runtime, NativeArgs args) {
964
0
  GCScope gcScope(runtime);
965
966
  // 1. Let O be ? ToObject(this value).
967
0
  auto objRes = toObject(runtime, args.getThisHandle());
968
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
969
0
    return ExecutionStatus::EXCEPTION;
970
0
  }
971
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
972
973
0
  MutableHandle<> len{runtime};
974
975
  // 2. Let len be ? ToLength(? Get(O, "length")).
976
0
  Handle<JSArray> arr = Handle<JSArray>::dyn_vmcast(O);
977
0
  if (LLVM_LIKELY(arr)) {
978
    // Fast path for getting the length.
979
0
    len = HermesValue::encodeTrustedNumberValue(
980
0
        JSArray::getLength(arr.get(), runtime));
981
0
  } else {
982
    // Slow path, used when pushing onto non-array objects.
983
0
    auto propRes = JSObject::getNamed_RJS(
984
0
        O, runtime, Predefined::getSymbolID(Predefined::length));
985
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
986
0
      return ExecutionStatus::EXCEPTION;
987
0
    }
988
0
    auto lenRes = toLength(runtime, runtime.makeHandle(std::move(*propRes)));
989
0
    if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
990
0
      return ExecutionStatus::EXCEPTION;
991
0
    }
992
0
    len = lenRes.getValue();
993
0
  }
994
995
  // 3. Let items be a List whose elements are, in left to right order, the
996
  // arguments that were passed to this function invocation.
997
  // 4. Let argCount be the number of elements in items.
998
0
  uint32_t argCount = args.getArgCount();
999
1000
  // 5. If len + argCount > 2**53-1, throw a TypeError exception.
1001
0
  if (len->getNumber() + (double)argCount > std::pow(2.0, 53) - 1) {
1002
0
    return runtime.raiseTypeError("Array length exceeded in push()");
1003
0
  }
1004
1005
0
  auto marker = gcScope.createMarker();
1006
  // 6. Repeat, while items is not empty
1007
0
  for (auto arg : args.handles()) {
1008
    // a. Remove the first element from items and let E be the value of the
1009
    // element.
1010
    // b. Perform ? Set(O, ! ToString(len), E, true).
1011
    // NOTE: If the prototype has an index-like non-writable property at
1012
    // index n, we have to fail to push.
1013
    // If the prototype has an index-like accessor at index n,
1014
    // then we have to attempt to call the setter.
1015
    // Must call putComputed because the array prototype could have values for
1016
    // keys that haven't been inserted into O yet.
1017
0
    if (LLVM_UNLIKELY(
1018
0
            JSObject::putComputed_RJS(
1019
0
                O, runtime, len, arg, PropOpFlags().plusThrowOnError()) ==
1020
0
            ExecutionStatus::EXCEPTION)) {
1021
0
      return ExecutionStatus::EXCEPTION;
1022
0
    }
1023
0
    gcScope.flushToMarker(marker);
1024
    // c. Let len be len+1.
1025
0
    len = HermesValue::encodeTrustedNumberValue(len->getNumber() + 1);
1026
0
  }
1027
1028
  // 7. Perform ? Set(O, "length", len, true).
1029
0
  if (LLVM_UNLIKELY(
1030
0
          JSObject::putNamed_RJS(
1031
0
              O,
1032
0
              runtime,
1033
0
              Predefined::getSymbolID(Predefined::length),
1034
0
              len,
1035
0
              PropOpFlags().plusThrowOnError()) ==
1036
0
          ExecutionStatus::EXCEPTION)) {
1037
0
    return ExecutionStatus::EXCEPTION;
1038
0
  }
1039
1040
  // 8. Return len.
1041
0
  return len.get();
1042
0
}
1043
1044
namespace {
1045
/// General object sorting model used by custom sorting routines.
1046
/// Provides a model by which to less and swap elements, using the [[Get]],
1047
/// [[Put]], and [[Delete]] internal methods of a supplied Object. Should be
1048
/// allocated on the stack, because it creates its own internal GCScope, with
1049
/// reusable MutableHandle<>-s that are used in the less and swap methods.
1050
/// These allow for quick accesses without allocating a great number of new
1051
/// handles every time we want to compare different elements.
1052
/// Usage example:
1053
///   StandardSortModel sm{runtime, obj, compareFn};
1054
///   quickSort(sm, 0, length);
1055
/// Note that this is generic and does nothing different if passed a JSArray.
1056
class StandardSortModel : public SortModel {
1057
 private:
1058
  /// Runtime to sort in.
1059
  Runtime &runtime_;
1060
1061
  /// Scope to allocate handles in, gets destroyed with this.
1062
  GCScope gcScope_;
1063
1064
  /// JS comparison function, return -1 for less, 0 for equal, 1 for greater.
1065
  /// If null, then use the built in < operator.
1066
  Handle<Callable> compareFn_;
1067
1068
  /// Object to sort elements [0, length).
1069
  Handle<JSObject> obj_;
1070
1071
  /// Temporary handles for property name.
1072
  MutableHandle<SymbolID> aTmpNameStorage_;
1073
  MutableHandle<SymbolID> bTmpNameStorage_;
1074
1075
  /// Preallocate handles in the current GCScope so that we don't have to make
1076
  /// new handles in every method call.
1077
1078
  /// Handles for two indices.
1079
  MutableHandle<> aHandle_;
1080
  MutableHandle<> bHandle_;
1081
1082
  /// Handles for the values at two indices.
1083
  MutableHandle<> aValue_;
1084
  MutableHandle<> bValue_;
1085
1086
  /// Handles for the objects the values are retrieved from.
1087
  MutableHandle<JSObject> aDescObjHandle_;
1088
  MutableHandle<JSObject> bDescObjHandle_;
1089
1090
  /// Marker created after initializing all fields so handles allocated later
1091
  /// can be flushed.
1092
  GCScope::Marker gcMarker_;
1093
1094
 public:
1095
  StandardSortModel(
1096
      Runtime &runtime,
1097
      Handle<JSObject> obj,
1098
      Handle<Callable> compareFn)
1099
      : runtime_(runtime),
1100
        gcScope_(runtime),
1101
        compareFn_(compareFn),
1102
        obj_(obj),
1103
        aTmpNameStorage_(runtime),
1104
        bTmpNameStorage_(runtime),
1105
        aHandle_(runtime),
1106
        bHandle_(runtime),
1107
        aValue_(runtime),
1108
        bValue_(runtime),
1109
        aDescObjHandle_(runtime),
1110
        bDescObjHandle_(runtime),
1111
0
        gcMarker_(gcScope_.createMarker()) {}
1112
1113
  /// Use getComputed and putComputed to swap the values at obj[a] and obj[b].
1114
0
  ExecutionStatus swap(uint32_t a, uint32_t b) override {
1115
    // Ensure that we don't leave here with any new handles.
1116
0
    GCScopeMarkerRAII gcMarker{gcScope_, gcMarker_};
1117
1118
0
    aHandle_ = HermesValue::encodeTrustedNumberValue(a);
1119
0
    bHandle_ = HermesValue::encodeTrustedNumberValue(b);
1120
1121
0
    ComputedPropertyDescriptor aDesc;
1122
0
    JSObject::getComputedPrimitiveDescriptor(
1123
0
        obj_, runtime_, aHandle_, aDescObjHandle_, aTmpNameStorage_, aDesc);
1124
1125
0
    if (aDescObjHandle_) {
1126
0
      if (LLVM_LIKELY(!aDesc.flags.proxyObject)) {
1127
0
        auto res = JSObject::getComputedPropertyValue_RJS(
1128
0
            obj_,
1129
0
            runtime_,
1130
0
            aDescObjHandle_,
1131
0
            aTmpNameStorage_,
1132
0
            aDesc,
1133
0
            aDescObjHandle_);
1134
0
        if (res == ExecutionStatus::EXCEPTION) {
1135
0
          return ExecutionStatus::EXCEPTION;
1136
0
        }
1137
0
        if (LLVM_LIKELY(!(*res)->isEmpty())) {
1138
0
          aValue_ = std::move(*res);
1139
0
        }
1140
0
      } else {
1141
0
        auto keyRes = toPropertyKey(runtime_, aHandle_);
1142
0
        if (keyRes == ExecutionStatus::EXCEPTION) {
1143
0
          return ExecutionStatus::EXCEPTION;
1144
0
        }
1145
0
        aHandle_ = keyRes->get();
1146
0
        CallResult<bool> hasPropRes = JSProxy::getOwnProperty(
1147
0
            aDescObjHandle_, runtime_, aHandle_, aDesc, nullptr);
1148
0
        if (hasPropRes == ExecutionStatus::EXCEPTION) {
1149
0
          return ExecutionStatus::EXCEPTION;
1150
0
        }
1151
0
        if (*hasPropRes) {
1152
0
          auto res =
1153
0
              JSProxy::getComputed(aDescObjHandle_, runtime_, aHandle_, obj_);
1154
0
          if (res == ExecutionStatus::EXCEPTION) {
1155
0
            return ExecutionStatus::EXCEPTION;
1156
0
          }
1157
0
          aValue_ = std::move(*res);
1158
0
        } else {
1159
0
          aDescObjHandle_ = nullptr;
1160
0
        }
1161
0
      }
1162
0
    }
1163
1164
0
    ComputedPropertyDescriptor bDesc;
1165
0
    JSObject::getComputedPrimitiveDescriptor(
1166
0
        obj_, runtime_, bHandle_, bDescObjHandle_, bTmpNameStorage_, bDesc);
1167
1168
0
    if (bDescObjHandle_) {
1169
0
      if (LLVM_LIKELY(!bDesc.flags.proxyObject)) {
1170
0
        auto res = JSObject::getComputedPropertyValue_RJS(
1171
0
            obj_,
1172
0
            runtime_,
1173
0
            bDescObjHandle_,
1174
0
            bTmpNameStorage_,
1175
0
            bDesc,
1176
0
            bDescObjHandle_);
1177
0
        if (res == ExecutionStatus::EXCEPTION) {
1178
0
          return ExecutionStatus::EXCEPTION;
1179
0
        }
1180
0
        if (LLVM_LIKELY(!(*res)->isEmpty())) {
1181
0
          bValue_ = std::move(*res);
1182
0
        }
1183
0
      } else {
1184
0
        auto keyRes = toPropertyKey(runtime_, bHandle_);
1185
0
        if (keyRes == ExecutionStatus::EXCEPTION) {
1186
0
          return ExecutionStatus::EXCEPTION;
1187
0
        }
1188
0
        bHandle_ = keyRes->get();
1189
0
        CallResult<bool> hasPropRes = JSProxy::getOwnProperty(
1190
0
            bDescObjHandle_, runtime_, bHandle_, bDesc, nullptr);
1191
0
        if (hasPropRes == ExecutionStatus::EXCEPTION) {
1192
0
          return ExecutionStatus::EXCEPTION;
1193
0
        }
1194
0
        if (*hasPropRes) {
1195
0
          auto res =
1196
0
              JSProxy::getComputed(bDescObjHandle_, runtime_, bHandle_, obj_);
1197
0
          if (res == ExecutionStatus::EXCEPTION) {
1198
0
            return ExecutionStatus::EXCEPTION;
1199
0
          }
1200
0
          bValue_ = std::move(*res);
1201
0
        } else {
1202
0
          bDescObjHandle_ = nullptr;
1203
0
        }
1204
0
      }
1205
0
    }
1206
1207
0
    if (bDescObjHandle_) {
1208
0
      if (LLVM_UNLIKELY(
1209
0
              JSObject::putComputed_RJS(
1210
0
                  obj_,
1211
0
                  runtime_,
1212
0
                  aHandle_,
1213
0
                  bValue_,
1214
0
                  PropOpFlags().plusThrowOnError()) ==
1215
0
              ExecutionStatus::EXCEPTION)) {
1216
0
        return ExecutionStatus::EXCEPTION;
1217
0
      }
1218
0
    } else {
1219
0
      if (LLVM_UNLIKELY(
1220
0
              JSObject::deleteComputed(
1221
0
                  obj_, runtime_, aHandle_, PropOpFlags().plusThrowOnError()) ==
1222
0
              ExecutionStatus::EXCEPTION)) {
1223
0
        return ExecutionStatus::EXCEPTION;
1224
0
      }
1225
0
    }
1226
1227
0
    if (aDescObjHandle_) {
1228
0
      if (LLVM_UNLIKELY(
1229
0
              JSObject::putComputed_RJS(
1230
0
                  obj_,
1231
0
                  runtime_,
1232
0
                  bHandle_,
1233
0
                  aValue_,
1234
0
                  PropOpFlags().plusThrowOnError()) ==
1235
0
              ExecutionStatus::EXCEPTION)) {
1236
0
        return ExecutionStatus::EXCEPTION;
1237
0
      }
1238
0
    } else {
1239
0
      if (LLVM_UNLIKELY(
1240
0
              JSObject::deleteComputed(
1241
0
                  obj_, runtime_, bHandle_, PropOpFlags().plusThrowOnError()) ==
1242
0
              ExecutionStatus::EXCEPTION)) {
1243
0
        return ExecutionStatus::EXCEPTION;
1244
0
      }
1245
0
    }
1246
1247
0
    return ExecutionStatus::RETURNED;
1248
0
  }
1249
1250
  /// If compareFn isn't null, return compareFn(obj[a], obj[b])
1251
  /// If compareFn is null, return -1 if obj[a] < obj[b], 1 if obj[a] > obj[b],
1252
  /// 0 otherwise
1253
0
  CallResult<int> compare(uint32_t a, uint32_t b) override {
1254
    // Ensure that we don't leave here with any new handles.
1255
0
    GCScopeMarkerRAII gcMarker{gcScope_, gcMarker_};
1256
1257
0
    aHandle_ = HermesValue::encodeTrustedNumberValue(a);
1258
0
    bHandle_ = HermesValue::encodeTrustedNumberValue(b);
1259
1260
0
    ComputedPropertyDescriptor aDesc;
1261
0
    JSObject::getComputedPrimitiveDescriptor(
1262
0
        obj_, runtime_, aHandle_, aDescObjHandle_, aTmpNameStorage_, aDesc);
1263
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
1264
0
        obj_, runtime_, aDescObjHandle_, aTmpNameStorage_, aDesc, aHandle_);
1265
0
    if (propRes == ExecutionStatus::EXCEPTION) {
1266
0
      return ExecutionStatus::EXCEPTION;
1267
0
    }
1268
0
    if ((*propRes)->isEmpty()) {
1269
      // Spec defines empty as greater than everything.
1270
0
      return 1;
1271
0
    }
1272
0
    aValue_ = std::move(*propRes);
1273
0
    assert(!aValue_->isEmpty());
1274
1275
0
    ComputedPropertyDescriptor bDesc;
1276
0
    JSObject::getComputedPrimitiveDescriptor(
1277
0
        obj_, runtime_, bHandle_, bDescObjHandle_, bTmpNameStorage_, bDesc);
1278
0
    if ((propRes = JSObject::getComputedPropertyValue_RJS(
1279
0
             obj_,
1280
0
             runtime_,
1281
0
             bDescObjHandle_,
1282
0
             bTmpNameStorage_,
1283
0
             bDesc,
1284
0
             bHandle_)) == ExecutionStatus::EXCEPTION) {
1285
0
      return ExecutionStatus::EXCEPTION;
1286
0
    }
1287
0
    if ((*propRes)->isEmpty()) {
1288
      // Spec defines empty as greater than everything.
1289
0
      return -1;
1290
0
    }
1291
0
    bValue_ = std::move(*propRes);
1292
0
    assert(!bValue_->isEmpty());
1293
1294
0
    if (aValue_->isUndefined()) {
1295
      // Spec defines undefined as greater than everything.
1296
0
      return 1;
1297
0
    }
1298
0
    if (bValue_->isUndefined()) {
1299
      // Spec defines undefined as greater than everything.
1300
0
      return -1;
1301
0
    }
1302
1303
0
    if (compareFn_) {
1304
      // If we have a compareFn, just use that.
1305
0
      auto callRes = Callable::executeCall2(
1306
0
          compareFn_,
1307
0
          runtime_,
1308
0
          Runtime::getUndefinedValue(),
1309
0
          aValue_.get(),
1310
0
          bValue_.get());
1311
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
1312
0
        return ExecutionStatus::EXCEPTION;
1313
0
      }
1314
0
      auto intRes =
1315
0
          toNumber_RJS(runtime_, runtime_.makeHandle(std::move(*callRes)));
1316
0
      if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1317
0
        return ExecutionStatus::EXCEPTION;
1318
0
      }
1319
      // Cannot return intRes's value directly because it can be NaN
1320
0
      auto res = intRes->getNumber();
1321
0
      return (res < 0) ? -1 : (res > 0 ? 1 : 0);
1322
0
    } else {
1323
      // Convert both arguments to strings and compare
1324
0
      auto aValueRes = toString_RJS(runtime_, aValue_);
1325
0
      if (LLVM_UNLIKELY(aValueRes == ExecutionStatus::EXCEPTION)) {
1326
0
        return ExecutionStatus::EXCEPTION;
1327
0
      }
1328
0
      aValue_ = aValueRes->getHermesValue();
1329
1330
0
      auto bValueRes = toString_RJS(runtime_, bValue_);
1331
0
      if (LLVM_UNLIKELY(bValueRes == ExecutionStatus::EXCEPTION)) {
1332
0
        return ExecutionStatus::EXCEPTION;
1333
0
      }
1334
0
      bValue_ = bValueRes->getHermesValue();
1335
1336
0
      return aValue_->getString()->compare(bValue_->getString());
1337
0
    }
1338
0
  }
1339
};
1340
1341
/// Perform a sort of a sparse object by querying its properties first.
1342
/// It cannot be a proxy or a host object because they are not guaranteed to
1343
/// be able to list their properties.
1344
CallResult<HermesValue> sortSparse(
1345
    Runtime &runtime,
1346
    Handle<JSObject> O,
1347
    Handle<Callable> compareFn,
1348
0
    uint64_t len) {
1349
0
  GCScope gcScope{runtime};
1350
1351
0
  assert(
1352
0
      !O->isHostObject() && !O->isProxyObject() &&
1353
0
      "only non-exotic objects can be sparsely sorted");
1354
1355
  // This is a "non-fast" object, meaning we need to create a symbol for every
1356
  // property name. On the assumption that it is sparse, get all properties
1357
  // first, so that we only have to read the existing properties.
1358
1359
0
  auto crNames = JSObject::getOwnPropertyNames(O, runtime, false);
1360
0
  if (crNames == ExecutionStatus::EXCEPTION)
1361
0
    return ExecutionStatus::EXCEPTION;
1362
  // Get the underlying storage containing the names.
1363
0
  auto names = runtime.makeHandle((*crNames)->getIndexedStorage(runtime));
1364
0
  if (!names) {
1365
    // Indexed storage can be null if there's nothing to store.
1366
0
    return O.getHermesValue();
1367
0
  }
1368
1369
  // Find out how many sortable numeric properties we have.
1370
0
  JSArray::StorageType::size_type numProps = 0;
1371
0
  for (JSArray::StorageType::size_type e = names->size(runtime); numProps != e;
1372
0
       ++numProps) {
1373
0
    SmallHermesValue hv = names->at(runtime, numProps);
1374
    // Stop at the first non-number.
1375
0
    if (!hv.isNumber())
1376
0
      break;
1377
    // Stop if the property name is beyond "len".
1378
0
    if (hv.getNumber(runtime) >= len)
1379
0
      break;
1380
0
  }
1381
1382
  // If we didn't find any numeric properties, there is nothing to do.
1383
0
  if (numProps == 0)
1384
0
    return O.getHermesValue();
1385
1386
  // Create a new array which we will actually sort.
1387
0
  auto crArray = JSArray::create(runtime, numProps, numProps);
1388
0
  if (crArray == ExecutionStatus::EXCEPTION)
1389
0
    return ExecutionStatus::EXCEPTION;
1390
0
  auto array = *crArray;
1391
0
  if (JSArray::setStorageEndIndex(array, runtime, numProps) ==
1392
0
      ExecutionStatus::EXCEPTION) {
1393
0
    return ExecutionStatus::EXCEPTION;
1394
0
  }
1395
1396
0
  MutableHandle<> propName{runtime};
1397
0
  MutableHandle<> propVal{runtime};
1398
0
  GCScopeMarkerRAII gcMarker{gcScope};
1399
1400
  // Copy all sortable properties into the array and delete them from the
1401
  // source. Deleting all sortable properties makes it easy to just copy the
1402
  // sorted result back in the end.
1403
0
  for (decltype(numProps) i = 0; i != numProps; ++i) {
1404
0
    gcMarker.flush();
1405
1406
0
    propName = names->at(runtime, i).unboxToHV(runtime);
1407
0
    auto res = JSObject::getComputed_RJS(O, runtime, propName);
1408
0
    if (res == ExecutionStatus::EXCEPTION)
1409
0
      return ExecutionStatus::EXCEPTION;
1410
    // Skip empty values.
1411
0
    if (res->getHermesValue().isEmpty())
1412
0
      continue;
1413
1414
0
    const auto shv = SmallHermesValue::encodeHermesValue(res->get(), runtime);
1415
0
    JSArray::unsafeSetExistingElementAt(*array, runtime, i, shv);
1416
1417
0
    if (JSObject::deleteComputed(
1418
0
            O, runtime, propName, PropOpFlags().plusThrowOnError()) ==
1419
0
        ExecutionStatus::EXCEPTION) {
1420
0
      return ExecutionStatus::EXCEPTION;
1421
0
    }
1422
0
  }
1423
0
  gcMarker.flush();
1424
1425
0
  {
1426
0
    StandardSortModel sm(runtime, array, compareFn);
1427
0
    if (LLVM_UNLIKELY(
1428
0
            quickSort(&sm, 0u, numProps) == ExecutionStatus::EXCEPTION))
1429
0
      return ExecutionStatus::EXCEPTION;
1430
0
  }
1431
1432
  // Time to copy back the values.
1433
0
  for (decltype(numProps) i = 0; i != numProps; ++i) {
1434
0
    gcMarker.flush();
1435
1436
0
    auto hv = array->at(runtime, i).unboxToHV(runtime);
1437
0
    assert(
1438
0
        !hv.isEmpty() &&
1439
0
        "empty values cannot appear in the array out of nowhere");
1440
0
    propVal = hv;
1441
1442
0
    propName = HermesValue::encodeTrustedNumberValue(i);
1443
1444
0
    if (JSObject::putComputed_RJS(
1445
0
            O, runtime, propName, propVal, PropOpFlags().plusThrowOnError()) ==
1446
0
        ExecutionStatus::EXCEPTION) {
1447
0
      return ExecutionStatus::EXCEPTION;
1448
0
    }
1449
0
  }
1450
1451
0
  return O.getHermesValue();
1452
0
}
1453
} // anonymous namespace
1454
1455
/// ES5.1 15.4.4.11.
1456
CallResult<HermesValue>
1457
0
arrayPrototypeSort(void *, Runtime &runtime, NativeArgs args) {
1458
  // Null if not a callable compareFn.
1459
0
  auto compareFn = Handle<Callable>::dyn_vmcast(args.getArgHandle(0));
1460
0
  if (!args.getArg(0).isUndefined() && !compareFn) {
1461
0
    return runtime.raiseTypeError("Array sort argument must be callable");
1462
0
  }
1463
1464
0
  auto objRes = toObject(runtime, args.getThisHandle());
1465
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
1466
0
    return ExecutionStatus::EXCEPTION;
1467
0
  }
1468
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
1469
1470
0
  auto propRes = JSObject::getNamed_RJS(
1471
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
1472
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1473
0
    return ExecutionStatus::EXCEPTION;
1474
0
  }
1475
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
1476
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1477
0
    return ExecutionStatus::EXCEPTION;
1478
0
  }
1479
0
  uint64_t len = *intRes;
1480
1481
  // If we are not sorting a regular dense array, use a special routine which
1482
  // first copies all properties into an array.
1483
  // Proxies  and host objects however are excluded because they are weird.
1484
0
  if (!O->isProxyObject() && !O->isHostObject() && !O->hasFastIndexProperties())
1485
0
    return sortSparse(runtime, O, compareFn, len);
1486
1487
  // This is the "fast" path. We are sorting an array with indexed storage.
1488
0
  StandardSortModel sm(runtime, O, compareFn);
1489
1490
  // Use our custom sort routine. We can't use std::sort because it performs
1491
  // optimizations that allow it to bypass calls to std::swap, but our swap
1492
  // function is special, since it needs to use the internal Object functions.
1493
0
  if (LLVM_UNLIKELY(quickSort(&sm, 0u, len) == ExecutionStatus::EXCEPTION))
1494
0
    return ExecutionStatus::EXCEPTION;
1495
1496
0
  return O.getHermesValue();
1497
0
}
1498
1499
inline CallResult<HermesValue>
1500
0
arrayPrototypeForEach(void *, Runtime &runtime, NativeArgs args) {
1501
0
  GCScope gcScope(runtime);
1502
0
  auto objRes = toObject(runtime, args.getThisHandle());
1503
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
1504
0
    return ExecutionStatus::EXCEPTION;
1505
0
  }
1506
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
1507
1508
0
  auto propRes = JSObject::getNamed_RJS(
1509
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
1510
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1511
0
    return ExecutionStatus::EXCEPTION;
1512
0
  }
1513
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
1514
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1515
0
    return ExecutionStatus::EXCEPTION;
1516
0
  }
1517
0
  uint64_t len = *intRes;
1518
1519
0
  auto callbackFn = args.dyncastArg<Callable>(0);
1520
0
  if (!callbackFn) {
1521
0
    return runtime.raiseTypeError(
1522
0
        "Array.prototype.forEach() requires a callable argument");
1523
0
  }
1524
1525
  // Index to execute the callback on.
1526
0
  MutableHandle<> k{runtime, HermesValue::encodeTrustedNumberValue(0)};
1527
1528
0
  MutableHandle<JSObject> descObjHandle{runtime};
1529
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
1530
1531
  // Loop through and execute the callback on all existing values.
1532
  // TODO: Implement a fast path for actual arrays.
1533
0
  auto marker = gcScope.createMarker();
1534
0
  while (k->getDouble() < len) {
1535
0
    gcScope.flushToMarker(marker);
1536
1537
0
    ComputedPropertyDescriptor desc;
1538
0
    JSObject::getComputedPrimitiveDescriptor(
1539
0
        O, runtime, k, descObjHandle, tmpPropNameStorage, desc);
1540
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
1541
0
        O, runtime, descObjHandle, tmpPropNameStorage, desc, k);
1542
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1543
0
      return ExecutionStatus::EXCEPTION;
1544
0
    }
1545
0
    if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
1546
0
      auto kValue = std::move(*propRes);
1547
0
      if (LLVM_UNLIKELY(
1548
0
              Callable::executeCall3(
1549
0
                  callbackFn,
1550
0
                  runtime,
1551
0
                  args.getArgHandle(1),
1552
0
                  kValue.get(),
1553
0
                  k.get(),
1554
0
                  O.getHermesValue()) == ExecutionStatus::EXCEPTION)) {
1555
0
        return ExecutionStatus::EXCEPTION;
1556
0
      }
1557
0
    }
1558
1559
0
    k = HermesValue::encodeTrustedNumberValue(k->getDouble() + 1);
1560
0
  }
1561
1562
0
  return HermesValue::encodeUndefinedValue();
1563
0
}
1564
1565
/// ES10 22.1.3.10.1 FlattenIntoArray
1566
/// mapperFunction may be null to signify its absence.
1567
/// If mapperFunction is null, thisArg is ignored.
1568
static CallResult<uint64_t> flattenIntoArray(
1569
    Runtime &runtime,
1570
    Handle<JSArray> target,
1571
    Handle<JSObject> source,
1572
    uint64_t sourceLen,
1573
    uint64_t start,
1574
    double depth,
1575
    Handle<Callable> mapperFunction,
1576
0
    Handle<> thisArg) {
1577
0
  ScopedNativeDepthTracker depthTracker{runtime};
1578
0
  if (LLVM_UNLIKELY(depthTracker.overflowed())) {
1579
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
1580
0
  }
1581
1582
0
  if (!mapperFunction) {
1583
0
    assert(
1584
0
        thisArg->isUndefined() &&
1585
0
        "thisArg must be undefined if there is no mapper");
1586
0
  }
1587
1588
0
  GCScope gcScope{runtime};
1589
  // 1. Let targetIndex be start.
1590
0
  uint64_t targetIndex = start;
1591
  // 2. Let sourceIndex be 0.
1592
0
  uint64_t sourceIndex = 0;
1593
1594
  // Temporary storage for sourceIndex and targetIndex.
1595
0
  MutableHandle<> indexHandle{runtime};
1596
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
1597
0
  MutableHandle<JSObject> propObj{runtime};
1598
0
  MutableHandle<> element{runtime};
1599
0
  MutableHandle<> lenResHandle{runtime};
1600
1601
0
  auto marker = gcScope.createMarker();
1602
1603
  // 3. Repeat, while sourceIndex < sourceLen
1604
0
  while (sourceIndex < sourceLen) {
1605
0
    gcScope.flushToMarker(marker);
1606
1607
    // a. Let P be ! ToString(sourceIndex).
1608
    // b. Let exists be ? HasProperty(source, P).
1609
0
    ComputedPropertyDescriptor desc{};
1610
0
    indexHandle = HermesValue::encodeTrustedNumberValue(sourceIndex);
1611
0
    if (LLVM_UNLIKELY(
1612
0
            JSObject::getComputedDescriptor(
1613
0
                source,
1614
0
                runtime,
1615
0
                indexHandle,
1616
0
                propObj,
1617
0
                tmpPropNameStorage,
1618
0
                desc) == ExecutionStatus::EXCEPTION)) {
1619
0
      return ExecutionStatus::EXCEPTION;
1620
0
    }
1621
    // c. If exists is true, then
1622
    // i. Let element be ? Get(source, P).
1623
0
    CallResult<PseudoHandle<>> elementRes =
1624
0
        JSObject::getComputedPropertyValue_RJS(
1625
0
            source, runtime, propObj, tmpPropNameStorage, desc, indexHandle);
1626
0
    if (LLVM_UNLIKELY(elementRes == ExecutionStatus::EXCEPTION)) {
1627
0
      return ExecutionStatus::EXCEPTION;
1628
0
    }
1629
0
    if (LLVM_LIKELY(!(*elementRes)->isEmpty())) {
1630
0
      element = std::move(*elementRes);
1631
1632
      // ii. If mapperFunction is present, then
1633
0
      if (mapperFunction) {
1634
        // 1. Assert: thisArg is present.
1635
0
        assert(!thisArg->isEmpty() && "mapperFunction requires a thisArg");
1636
        // 2. Set element to ? Call(mapperFunction, thisArg , « element,
1637
        // sourceIndex, source »).
1638
0
        elementRes = Callable::executeCall3(
1639
0
            mapperFunction,
1640
0
            runtime,
1641
0
            thisArg,
1642
0
            element.getHermesValue(),
1643
0
            HermesValue::encodeTrustedNumberValue(sourceIndex),
1644
0
            source.getHermesValue());
1645
0
        if (LLVM_UNLIKELY(elementRes == ExecutionStatus::EXCEPTION)) {
1646
0
          return ExecutionStatus::EXCEPTION;
1647
0
        }
1648
0
        element = std::move(*elementRes);
1649
0
      }
1650
      // iii. Let shouldFlatten be false.
1651
0
      bool shouldFlatten = false;
1652
0
      if (depth > 0) {
1653
        // iv. If depth > 0, then
1654
        // 1. Set shouldFlatten to ? IsArray(element).
1655
        // NOTE: isArray accepts nullptr for the obj argument.
1656
0
        CallResult<bool> shouldFlattenRes =
1657
0
            isArray(runtime, dyn_vmcast<JSObject>(element.get()));
1658
0
        if (LLVM_UNLIKELY(shouldFlattenRes == ExecutionStatus::EXCEPTION)) {
1659
0
          return ExecutionStatus::EXCEPTION;
1660
0
        }
1661
0
        shouldFlatten = *shouldFlattenRes;
1662
0
      }
1663
0
      if (shouldFlatten) {
1664
        // It is valid to cast `element` to JSObject because shouldFlatten is
1665
        // only true when `isArray(element)` is true.
1666
        // v. If shouldFlatten is true, then
1667
        // 1. Let elementLen be ? ToLength(? Get(element, "length")).
1668
0
        CallResult<PseudoHandle<>> lenRes = JSObject::getNamed_RJS(
1669
0
            Handle<JSObject>::vmcast(element),
1670
0
            runtime,
1671
0
            Predefined::getSymbolID(Predefined::length));
1672
0
        if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
1673
0
          return ExecutionStatus::EXCEPTION;
1674
0
        }
1675
0
        lenResHandle = std::move(*lenRes);
1676
0
        CallResult<uint64_t> elementLenRes = toLengthU64(runtime, lenResHandle);
1677
0
        if (LLVM_UNLIKELY(elementLenRes == ExecutionStatus::EXCEPTION)) {
1678
0
          return ExecutionStatus::EXCEPTION;
1679
0
        }
1680
0
        uint64_t elementLen = *elementLenRes;
1681
        // 2. Set targetIndex to ? FlattenIntoArray(target, element, elementLen,
1682
        // targetIndex, depth - 1).
1683
0
        CallResult<uint64_t> targetIndexRes = flattenIntoArray(
1684
0
            runtime,
1685
0
            target,
1686
0
            Handle<JSObject>::vmcast(element),
1687
0
            elementLen,
1688
0
            targetIndex,
1689
0
            depth - 1,
1690
0
            runtime.makeNullHandle<Callable>(),
1691
0
            runtime.getUndefinedValue());
1692
0
        if (LLVM_UNLIKELY(targetIndexRes == ExecutionStatus::EXCEPTION)) {
1693
0
          return ExecutionStatus::EXCEPTION;
1694
0
        }
1695
0
        targetIndex = *targetIndexRes;
1696
0
      } else {
1697
        // vi. Else,
1698
        // 1. If targetIndex ≥ 2**53-1, throw a TypeError exception.
1699
0
        if (targetIndex >= ((uint64_t)1 << 53) - 1) {
1700
0
          return runtime.raiseTypeError("flattened array exceeds length limit");
1701
0
        }
1702
        // 2. Perform ? CreateDataPropertyOrThrow(
1703
        //                target, !ToString(targetIndex), element).
1704
0
        indexHandle = HermesValue::encodeTrustedNumberValue(targetIndex);
1705
0
        if (LLVM_UNLIKELY(
1706
0
                JSObject::defineOwnComputed(
1707
0
                    target,
1708
0
                    runtime,
1709
0
                    indexHandle,
1710
0
                    DefinePropertyFlags::getDefaultNewPropertyFlags(),
1711
0
                    element,
1712
0
                    PropOpFlags().plusThrowOnError()) ==
1713
0
                ExecutionStatus::EXCEPTION)) {
1714
0
          return ExecutionStatus::EXCEPTION;
1715
0
        }
1716
1717
        // 3. Increase targetIndex by 1.
1718
0
        ++targetIndex;
1719
0
      }
1720
0
    }
1721
    // d. Increase sourceIndex by 1.
1722
0
    ++sourceIndex;
1723
0
  }
1724
  // 4. Return targetIndex.
1725
0
  return targetIndex;
1726
0
}
1727
1728
CallResult<HermesValue>
1729
0
arrayPrototypeFlat(void *ctx, Runtime &runtime, NativeArgs args) {
1730
  // 1. Let O be ? ToObject(this value).
1731
0
  CallResult<HermesValue> ORes = toObject(runtime, args.getThisHandle());
1732
0
  if (LLVM_UNLIKELY(ORes == ExecutionStatus::EXCEPTION)) {
1733
0
    return ExecutionStatus::EXCEPTION;
1734
0
  }
1735
0
  auto O = runtime.makeHandle<JSObject>(*ORes);
1736
1737
  // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
1738
0
  CallResult<PseudoHandle<>> lenRes = JSObject::getNamed_RJS(
1739
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
1740
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
1741
0
    return ExecutionStatus::EXCEPTION;
1742
0
  }
1743
0
  CallResult<uint64_t> sourceLenRes =
1744
0
      toLengthU64(runtime, runtime.makeHandle(std::move(*lenRes)));
1745
0
  if (LLVM_UNLIKELY(sourceLenRes == ExecutionStatus::EXCEPTION)) {
1746
0
    return ExecutionStatus::EXCEPTION;
1747
0
  }
1748
0
  uint64_t sourceLen = *sourceLenRes;
1749
1750
  // 3. Let depthNum be 1.
1751
0
  double depthNum = 1;
1752
0
  if (!args.getArg(0).isUndefined()) {
1753
    // 4. If depth is not undefined, then
1754
    // a.     Set depthNum to ? ToIntegerOrInfinity(depth).
1755
0
    auto depthNumRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
1756
0
    if (LLVM_UNLIKELY(depthNumRes == ExecutionStatus::EXCEPTION)) {
1757
0
      return ExecutionStatus::EXCEPTION;
1758
0
    }
1759
0
    depthNum = depthNumRes->getNumber();
1760
0
  }
1761
  // 5. Let A be ? ArraySpeciesCreate(O, 0).
1762
0
  auto ARes = JSArray::create(runtime, 0, 0);
1763
0
  if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) {
1764
0
    return ExecutionStatus::EXCEPTION;
1765
0
  }
1766
0
  auto A = *ARes;
1767
1768
  // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
1769
0
  if (LLVM_UNLIKELY(
1770
0
          flattenIntoArray(
1771
0
              runtime,
1772
0
              A,
1773
0
              O,
1774
0
              sourceLen,
1775
0
              0,
1776
0
              depthNum,
1777
0
              runtime.makeNullHandle<Callable>(),
1778
0
              runtime.getUndefinedValue()) == ExecutionStatus::EXCEPTION)) {
1779
0
    return ExecutionStatus::EXCEPTION;
1780
0
  }
1781
1782
  // 7. Return A.
1783
0
  return A.getHermesValue();
1784
0
}
1785
1786
CallResult<HermesValue>
1787
0
arrayPrototypeFlatMap(void *ctx, Runtime &runtime, NativeArgs args) {
1788
  // 1. Let O be ? ToObject(this value).
1789
0
  CallResult<HermesValue> ORes = toObject(runtime, args.getThisHandle());
1790
0
  if (LLVM_UNLIKELY(ORes == ExecutionStatus::EXCEPTION)) {
1791
0
    return ExecutionStatus::EXCEPTION;
1792
0
  }
1793
0
  auto O = runtime.makeHandle<JSObject>(*ORes);
1794
1795
  // 2. Let sourceLen be ? ToLength(? Get(O, "length")).
1796
0
  CallResult<PseudoHandle<>> lenRes = JSObject::getNamed_RJS(
1797
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
1798
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
1799
0
    return ExecutionStatus::EXCEPTION;
1800
0
  }
1801
0
  CallResult<uint64_t> sourceLenRes =
1802
0
      toLengthU64(runtime, runtime.makeHandle(std::move(*lenRes)));
1803
0
  if (LLVM_UNLIKELY(sourceLenRes == ExecutionStatus::EXCEPTION)) {
1804
0
    return ExecutionStatus::EXCEPTION;
1805
0
  }
1806
0
  uint64_t sourceLen = *sourceLenRes;
1807
1808
  // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
1809
0
  Handle<Callable> mapperFunction = args.dyncastArg<Callable>(0);
1810
0
  if (!mapperFunction) {
1811
0
    return runtime.raiseTypeError("flatMap mapper must be callable");
1812
0
  }
1813
  // 4. If thisArg is present, let T be thisArg; else let T be undefined.
1814
0
  auto T = args.getArgHandle(1);
1815
  // 5. Let A be ? ArraySpeciesCreate(O, 0).
1816
0
  auto ARes = JSArray::create(runtime, 0, 0);
1817
0
  if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) {
1818
0
    return ExecutionStatus::EXCEPTION;
1819
0
  }
1820
0
  auto A = *ARes;
1821
1822
  // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T).
1823
0
  if (LLVM_UNLIKELY(
1824
0
          flattenIntoArray(runtime, A, O, sourceLen, 0, 1, mapperFunction, T) ==
1825
0
          ExecutionStatus::EXCEPTION)) {
1826
0
    return ExecutionStatus::EXCEPTION;
1827
0
  }
1828
  // 7. Return A.
1829
0
  return A.getHermesValue();
1830
0
}
1831
1832
CallResult<HermesValue>
1833
0
arrayPrototypeIterator(void *ctx, Runtime &runtime, NativeArgs args) {
1834
0
  IterationKind kind = *reinterpret_cast<IterationKind *>(&ctx);
1835
0
  assert(
1836
0
      kind < IterationKind::NumKinds &&
1837
0
      "arrayPrototypeIterator with wrong kind");
1838
0
  auto objRes = toObject(runtime, args.getThisHandle());
1839
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
1840
0
    return ExecutionStatus::EXCEPTION;
1841
0
  }
1842
0
  auto obj = runtime.makeHandle<JSObject>(*objRes);
1843
0
  return JSArrayIterator::create(runtime, obj, kind).getHermesValue();
1844
0
}
1845
1846
CallResult<HermesValue>
1847
0
arrayPrototypeSlice(void *, Runtime &runtime, NativeArgs args) {
1848
0
  GCScope gcScope(runtime);
1849
0
  auto objRes = toObject(runtime, args.getThisHandle());
1850
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
1851
0
    return ExecutionStatus::EXCEPTION;
1852
0
  }
1853
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
1854
1855
0
  auto propRes = JSObject::getNamed_RJS(
1856
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
1857
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1858
0
    return ExecutionStatus::EXCEPTION;
1859
0
  }
1860
0
  auto lenRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
1861
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
1862
0
    return ExecutionStatus::EXCEPTION;
1863
0
  }
1864
0
  double len = *lenRes;
1865
1866
0
  auto intRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
1867
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1868
0
    return ExecutionStatus::EXCEPTION;
1869
0
  }
1870
  // Start index. If negative, then offset from the right side of the array.
1871
0
  double relativeStart = intRes->getNumber();
1872
  // Index that we're currently copying from.
1873
  // Starts at the actual start value, computed from relativeStart.
1874
0
  MutableHandle<> k{
1875
0
      runtime,
1876
0
      HermesValue::encodeTrustedNumberValue(
1877
0
          relativeStart < 0 ? std::max(len + relativeStart, 0.0)
1878
0
                            : std::min(relativeStart, len))};
1879
1880
  // End index. If negative, then offset from the right side of the array.
1881
0
  double relativeEnd;
1882
0
  if (args.getArg(1).isUndefined()) {
1883
0
    relativeEnd = len;
1884
0
  } else {
1885
0
    if (LLVM_UNLIKELY(
1886
0
            (intRes = toIntegerOrInfinity(runtime, args.getArgHandle(1))) ==
1887
0
            ExecutionStatus::EXCEPTION)) {
1888
0
      return ExecutionStatus::EXCEPTION;
1889
0
    }
1890
0
    relativeEnd = intRes->getNumber();
1891
0
  }
1892
  // Actual end index.
1893
0
  double fin = relativeEnd < 0 ? std::max(len + relativeEnd, 0.0)
1894
0
                               : std::min(relativeEnd, len);
1895
1896
  // Create the result array.
1897
0
  double count = std::max(fin - k->getNumber(), 0.0);
1898
0
  if (LLVM_UNLIKELY(count > JSArray::StorageType::maxElements())) {
1899
0
    return runtime.raiseRangeError("Out of memory for array elements.");
1900
0
  }
1901
0
  auto arrRes = JSArray::create(runtime, count, count);
1902
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
1903
0
    return ExecutionStatus::EXCEPTION;
1904
0
  }
1905
0
  auto A = *arrRes;
1906
1907
  // Next index in A to write to.
1908
0
  uint32_t n = 0;
1909
1910
0
  MutableHandle<JSObject> descObjHandle{runtime};
1911
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
1912
0
  MutableHandle<> kValue{runtime};
1913
0
  auto marker = gcScope.createMarker();
1914
1915
  // Copy the elements between the actual start and end indices into A.
1916
  // TODO: Implement a fast path for actual arrays.
1917
0
  while (k->getNumber() < fin) {
1918
0
    ComputedPropertyDescriptor desc;
1919
0
    JSObject::getComputedPrimitiveDescriptor(
1920
0
        O, runtime, k, descObjHandle, tmpPropNameStorage, desc);
1921
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
1922
0
        O, runtime, descObjHandle, tmpPropNameStorage, desc, k);
1923
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1924
0
      return ExecutionStatus::EXCEPTION;
1925
0
    }
1926
0
    if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
1927
0
      kValue = std::move(*propRes);
1928
0
      JSArray::setElementAt(A, runtime, n, kValue);
1929
0
    }
1930
0
    k = HermesValue::encodeUntrustedNumberValue(k->getNumber() + 1);
1931
0
    ++n;
1932
1933
0
    gcScope.flushToMarker(marker);
1934
0
  }
1935
1936
0
  if (LLVM_UNLIKELY(
1937
0
          JSArray::setLengthProperty(A, runtime, n) ==
1938
0
          ExecutionStatus::EXCEPTION))
1939
0
    return ExecutionStatus::EXCEPTION;
1940
0
  return A.getHermesValue();
1941
0
}
1942
1943
CallResult<HermesValue>
1944
0
arrayPrototypeSplice(void *, Runtime &runtime, NativeArgs args) {
1945
0
  GCScope gcScope(runtime);
1946
0
  auto objRes = toObject(runtime, args.getThisHandle());
1947
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
1948
0
    return ExecutionStatus::EXCEPTION;
1949
0
  }
1950
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
1951
1952
0
  auto propRes = JSObject::getNamed_RJS(
1953
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
1954
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1955
0
    return ExecutionStatus::EXCEPTION;
1956
0
  }
1957
0
  auto lenRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
1958
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
1959
0
    return ExecutionStatus::EXCEPTION;
1960
0
  }
1961
0
  double len = *lenRes;
1962
1963
0
  auto intRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
1964
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1965
0
    return ExecutionStatus::EXCEPTION;
1966
0
  }
1967
0
  double relativeStart = intRes->getNumber();
1968
  // Index to start the deletion/insertion at.
1969
0
  double actualStart = relativeStart < 0 ? std::max(len + relativeStart, 0.0)
1970
0
                                         : std::min(relativeStart, len);
1971
1972
  // Implement the newer calculation of actualDeleteCount (ES6.0),
1973
  // since 5.1 doesn't define behavior for less than 2 arguments.
1974
0
  uint32_t argCount = args.getArgCount();
1975
0
  uint64_t actualDeleteCount;
1976
0
  uint64_t insertCount;
1977
0
  switch (argCount) {
1978
0
    case 0:
1979
0
      insertCount = 0;
1980
0
      actualDeleteCount = 0;
1981
0
      break;
1982
0
    case 1:
1983
      // If just one argument specified, delete everything until the end.
1984
0
      insertCount = 0;
1985
0
      actualDeleteCount = len - actualStart;
1986
0
      break;
1987
0
    default:
1988
      // Otherwise, use the specified delete count.
1989
0
      if (LLVM_UNLIKELY(
1990
0
              (intRes = toIntegerOrInfinity(runtime, args.getArgHandle(1))) ==
1991
0
              ExecutionStatus::EXCEPTION)) {
1992
0
        return ExecutionStatus::EXCEPTION;
1993
0
      }
1994
0
      insertCount = argCount - 2;
1995
0
      actualDeleteCount =
1996
0
          std::min(std::max(intRes->getNumber(), 0.0), len - actualStart);
1997
0
  }
1998
1999
  // If len+insertCount−actualDeleteCount > 2^53-1, throw a TypeError exception.
2000
  // Checks for overflow as well.
2001
0
  auto lenAfterInsert = len + insertCount;
2002
0
  if (LLVM_UNLIKELY(
2003
0
          lenAfterInsert < len ||
2004
0
          lenAfterInsert - actualDeleteCount > (1LLU << 53) - 1)) {
2005
0
    return runtime.raiseTypeError("Array.prototype.splice result out of space");
2006
0
  }
2007
2008
  // Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
2009
0
  if (LLVM_UNLIKELY(actualDeleteCount > JSArray::StorageType::maxElements())) {
2010
0
    return runtime.raiseRangeError("Out of memory for array elements.");
2011
0
  }
2012
0
  auto arrRes = JSArray::create(runtime, actualDeleteCount, actualDeleteCount);
2013
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
2014
0
    return ExecutionStatus::EXCEPTION;
2015
0
  }
2016
0
  auto A = *arrRes;
2017
2018
  // Indices used for various copies in loops below.
2019
0
  MutableHandle<> from{runtime};
2020
0
  MutableHandle<> to{runtime};
2021
2022
  // Value storage used for copying values.
2023
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
2024
0
  MutableHandle<JSObject> fromDescObjHandle{runtime};
2025
0
  MutableHandle<> fromValue{runtime};
2026
2027
0
  MutableHandle<> i{runtime};
2028
0
  MutableHandle<> k{runtime};
2029
2030
0
  auto gcMarker = gcScope.createMarker();
2031
2032
0
  {
2033
    // Copy actualDeleteCount elements to A, starting at actualStart.
2034
    // TODO: Add a fast path for actual arrays.
2035
0
    for (uint32_t j = 0; j < actualDeleteCount; ++j) {
2036
0
      from = HermesValue::encodeTrustedNumberValue(actualStart + j);
2037
2038
0
      ComputedPropertyDescriptor fromDesc;
2039
0
      JSObject::getComputedPrimitiveDescriptor(
2040
0
          O, runtime, from, fromDescObjHandle, tmpPropNameStorage, fromDesc);
2041
0
      CallResult<PseudoHandle<>> propRes =
2042
0
          JSObject::getComputedPropertyValue_RJS(
2043
0
              O,
2044
0
              runtime,
2045
0
              fromDescObjHandle,
2046
0
              tmpPropNameStorage,
2047
0
              fromDesc,
2048
0
              from);
2049
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2050
0
        return ExecutionStatus::EXCEPTION;
2051
0
      }
2052
0
      if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
2053
0
        fromValue = std::move(*propRes);
2054
0
        JSArray::setElementAt(A, runtime, j, fromValue);
2055
0
      }
2056
2057
0
      gcScope.flushToMarker(gcMarker);
2058
0
    }
2059
2060
0
    if (LLVM_UNLIKELY(
2061
0
            JSArray::setLengthProperty(A, runtime, actualDeleteCount) ==
2062
0
            ExecutionStatus::EXCEPTION))
2063
0
      return ExecutionStatus::EXCEPTION;
2064
0
  }
2065
2066
  // Perform ? Set(A, "length", actualDeleteCount, true).
2067
0
  if (LLVM_UNLIKELY(
2068
0
          JSObject::putNamed_RJS(
2069
0
              A,
2070
0
              runtime,
2071
0
              Predefined::getSymbolID(Predefined::length),
2072
0
              runtime.makeHandle(
2073
0
                  HermesValue::encodeUntrustedNumberValue(actualDeleteCount)),
2074
0
              PropOpFlags().plusThrowOnError()) ==
2075
0
          ExecutionStatus::EXCEPTION)) {
2076
0
    return ExecutionStatus::EXCEPTION;
2077
0
  }
2078
2079
  // Number of new items to add to the array.
2080
0
  uint32_t itemCount = args.getArgCount() > 2 ? args.getArgCount() - 2 : 0;
2081
2082
0
  if (itemCount < actualDeleteCount) {
2083
    // Inserting less items than deleting.
2084
2085
    // Copy items from (k + actualDeleteCount) to (k + itemCount).
2086
    // This leaves itemCount spaces to copy the arguments into.
2087
    // TODO: Add a fast path for actual arrays.
2088
0
    for (double j = actualStart; j < len - actualDeleteCount; ++j) {
2089
0
      from = HermesValue::encodeUntrustedNumberValue(j + actualDeleteCount);
2090
0
      to = HermesValue::encodeUntrustedNumberValue(j + itemCount);
2091
0
      ComputedPropertyDescriptor fromDesc;
2092
0
      JSObject::getComputedPrimitiveDescriptor(
2093
0
          O, runtime, from, fromDescObjHandle, tmpPropNameStorage, fromDesc);
2094
0
      CallResult<PseudoHandle<>> propRes =
2095
0
          JSObject::getComputedPropertyValue_RJS(
2096
0
              O,
2097
0
              runtime,
2098
0
              fromDescObjHandle,
2099
0
              tmpPropNameStorage,
2100
0
              fromDesc,
2101
0
              from);
2102
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2103
0
        return ExecutionStatus::EXCEPTION;
2104
0
      }
2105
0
      if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
2106
0
        fromValue = std::move(*propRes);
2107
0
        if (LLVM_UNLIKELY(
2108
0
                JSObject::putComputed_RJS(
2109
0
                    O,
2110
0
                    runtime,
2111
0
                    to,
2112
0
                    fromValue,
2113
0
                    PropOpFlags().plusThrowOnError()) ==
2114
0
                ExecutionStatus::EXCEPTION)) {
2115
0
          return ExecutionStatus::EXCEPTION;
2116
0
        }
2117
0
      } else {
2118
0
        if (LLVM_UNLIKELY(
2119
0
                JSObject::deleteComputed(
2120
0
                    O, runtime, to, PropOpFlags().plusThrowOnError()) ==
2121
0
                ExecutionStatus::EXCEPTION)) {
2122
0
          return ExecutionStatus::EXCEPTION;
2123
0
        }
2124
0
      }
2125
2126
0
      gcScope.flushToMarker(gcMarker);
2127
0
    }
2128
2129
    // Use i here to refer to (k-1) in the spec, and reindex the loop.
2130
0
    i = HermesValue::encodeUntrustedNumberValue(len - 1);
2131
2132
    // Delete the remaining elements from the right that we didn't copy into.
2133
    // TODO: Add a fast path for actual arrays.
2134
0
    while (i->getNumber() > len - actualDeleteCount + itemCount - 1) {
2135
0
      if (LLVM_UNLIKELY(
2136
0
              JSObject::deleteComputed(
2137
0
                  O, runtime, i, PropOpFlags().plusThrowOnError()) ==
2138
0
              ExecutionStatus::EXCEPTION)) {
2139
0
        return ExecutionStatus::EXCEPTION;
2140
0
      }
2141
0
      i = HermesValue::encodeUntrustedNumberValue(i->getDouble() - 1);
2142
0
      gcScope.flushToMarker(gcMarker);
2143
0
    }
2144
0
  } else if (itemCount > actualDeleteCount) {
2145
    // Inserting more items than deleting.
2146
2147
    // Start from the right, and copy elements to the right.
2148
    // This makes space to insert the elements from the arguments.
2149
    // TODO: Add a fast path for actual arrays.
2150
0
    for (double j = len - actualDeleteCount; j > actualStart; --j) {
2151
0
      from = HermesValue::encodeUntrustedNumberValue(j + actualDeleteCount - 1);
2152
0
      to = HermesValue::encodeUntrustedNumberValue(j + itemCount - 1);
2153
2154
0
      ComputedPropertyDescriptor fromDesc;
2155
0
      JSObject::getComputedPrimitiveDescriptor(
2156
0
          O, runtime, from, fromDescObjHandle, tmpPropNameStorage, fromDesc);
2157
0
      CallResult<PseudoHandle<>> propRes =
2158
0
          JSObject::getComputedPropertyValue_RJS(
2159
0
              O,
2160
0
              runtime,
2161
0
              fromDescObjHandle,
2162
0
              tmpPropNameStorage,
2163
0
              fromDesc,
2164
0
              from);
2165
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2166
0
        return ExecutionStatus::EXCEPTION;
2167
0
      }
2168
0
      if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
2169
0
        fromValue = std::move(*propRes);
2170
0
        if (LLVM_UNLIKELY(
2171
0
                JSObject::putComputed_RJS(
2172
0
                    O,
2173
0
                    runtime,
2174
0
                    to,
2175
0
                    fromValue,
2176
0
                    PropOpFlags().plusThrowOnError()) ==
2177
0
                ExecutionStatus::EXCEPTION)) {
2178
0
          return ExecutionStatus::EXCEPTION;
2179
0
        }
2180
0
      } else {
2181
        // fromPresent is false
2182
0
        if (LLVM_UNLIKELY(
2183
0
                JSObject::deleteComputed(
2184
0
                    O, runtime, to, PropOpFlags().plusThrowOnError()) ==
2185
0
                ExecutionStatus::EXCEPTION)) {
2186
0
          return ExecutionStatus::EXCEPTION;
2187
0
        }
2188
0
      }
2189
2190
0
      gcScope.flushToMarker(gcMarker);
2191
0
    }
2192
0
  }
2193
2194
0
  {
2195
    // Finally, just copy the elements from the args into the array.
2196
    // TODO: Add a fast path for actual arrays.
2197
0
    k = HermesValue::encodeUntrustedNumberValue(actualStart);
2198
0
    for (size_t j = 2; j < argCount; ++j) {
2199
0
      if (LLVM_UNLIKELY(
2200
0
              JSObject::putComputed_RJS(
2201
0
                  O,
2202
0
                  runtime,
2203
0
                  k,
2204
0
                  args.getArgHandle(j),
2205
0
                  PropOpFlags().plusThrowOnError()) ==
2206
0
              ExecutionStatus::EXCEPTION)) {
2207
0
        return ExecutionStatus::EXCEPTION;
2208
0
      }
2209
0
      k = HermesValue::encodeUntrustedNumberValue(k->getDouble() + 1);
2210
0
      gcScope.flushToMarker(gcMarker);
2211
0
    }
2212
0
  }
2213
2214
0
  if (LLVM_UNLIKELY(
2215
0
          JSObject::putNamed_RJS(
2216
0
              O,
2217
0
              runtime,
2218
0
              Predefined::getSymbolID(Predefined::length),
2219
0
              runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(
2220
0
                  len - actualDeleteCount + itemCount)),
2221
0
              PropOpFlags().plusThrowOnError()) ==
2222
0
          ExecutionStatus::EXCEPTION)) {
2223
0
    return ExecutionStatus::EXCEPTION;
2224
0
  }
2225
2226
0
  return A.getHermesValue();
2227
0
}
2228
2229
CallResult<HermesValue>
2230
0
arrayPrototypeCopyWithin(void *, Runtime &runtime, NativeArgs args) {
2231
0
  GCScope gcScope{runtime};
2232
2233
  // 1. Let O be ToObject(this value).
2234
  // 2. ReturnIfAbrupt(O).
2235
0
  auto oRes = toObject(runtime, args.getThisHandle());
2236
0
  if (LLVM_UNLIKELY(oRes == ExecutionStatus::EXCEPTION)) {
2237
0
    return ExecutionStatus::EXCEPTION;
2238
0
  }
2239
0
  auto O = runtime.makeHandle<JSObject>(*oRes);
2240
2241
  // 3. Let len be ToLength(Get(O, "length")).
2242
  // 4. ReturnIfAbrupt(len).
2243
  // Use doubles for all lengths and indices to allow for proper Infinity
2244
  // handling, because ToInteger may return Infinity and we must do double
2245
  // arithmetic.
2246
0
  auto propRes = JSObject::getNamed_RJS(
2247
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2248
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2249
0
    return ExecutionStatus::EXCEPTION;
2250
0
  }
2251
0
  auto lenRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2252
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
2253
0
    return ExecutionStatus::EXCEPTION;
2254
0
  }
2255
0
  double len = *lenRes;
2256
2257
  // 5. Let relativeTarget be ToIntegerOrInfinity(target).
2258
  // 6. ReturnIfAbrupt(relativeTarget).
2259
0
  auto relativeTargetRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
2260
0
  if (LLVM_UNLIKELY(relativeTargetRes == ExecutionStatus::EXCEPTION)) {
2261
0
    return ExecutionStatus::EXCEPTION;
2262
0
  }
2263
0
  double relativeTarget = relativeTargetRes->getNumber();
2264
2265
  // 7. If relativeTarget < 0, let to be max((len + relativeTarget),0); else let
2266
  // to be min(relativeTarget, len).
2267
0
  double to = relativeTarget < 0 ? std::max((len + relativeTarget), (double)0)
2268
0
                                 : std::min(relativeTarget, len);
2269
2270
  // 8. Let relativeStart be ToIntegerOrInfinity(start).
2271
  // 9. ReturnIfAbrupt(relativeStart).
2272
0
  auto relativeStartRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
2273
0
  if (LLVM_UNLIKELY(relativeStartRes == ExecutionStatus::EXCEPTION)) {
2274
0
    return ExecutionStatus::EXCEPTION;
2275
0
  }
2276
0
  double relativeStart = relativeStartRes->getNumber();
2277
2278
  // 10. If relativeStart < 0, let from be max((len + relativeStart),0); else
2279
  // let from be min(relativeStart, len).
2280
0
  double from = relativeStart < 0 ? std::max((len + relativeStart), (double)0)
2281
0
                                  : std::min(relativeStart, len);
2282
2283
  // 11. If end is undefined, let relativeEnd be len; else let relativeEnd be
2284
  // ToIntegerOrInfinity(end).
2285
  // 12. ReturnIfAbrupt(relativeEnd).
2286
0
  double relativeEnd;
2287
0
  if (args.getArg(2).isUndefined()) {
2288
0
    relativeEnd = len;
2289
0
  } else {
2290
0
    auto relativeEndRes = toIntegerOrInfinity(runtime, args.getArgHandle(2));
2291
0
    if (LLVM_UNLIKELY(relativeEndRes == ExecutionStatus::EXCEPTION)) {
2292
0
      return ExecutionStatus::EXCEPTION;
2293
0
    }
2294
0
    relativeEnd = relativeEndRes->getNumber();
2295
0
  }
2296
2297
  // 13. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let
2298
  // final be min(relativeEnd, len).
2299
0
  double fin = relativeEnd < 0 ? std::max((len + relativeEnd), (double)0)
2300
0
                               : std::min(relativeEnd, len);
2301
2302
  // 14. Let count be min(final-from, len-to).
2303
0
  double count = std::min(fin - from, len - to);
2304
2305
0
  int direction;
2306
0
  if (from < to && to < from + count) {
2307
    // 15. If from<to and to<from+count
2308
    // a. Let direction be -1.
2309
0
    direction = -1;
2310
    // b. Let from be from + count -1.
2311
0
    from = from + count - 1;
2312
    // c. Let to be to + count -1.
2313
0
    to = to + count - 1;
2314
0
  } else {
2315
    // 16. Else,
2316
    // a. Let direction = 1.
2317
0
    direction = 1;
2318
0
  }
2319
2320
0
  MutableHandle<> fromHandle{
2321
0
      runtime, HermesValue::encodeUntrustedNumberValue(from)};
2322
0
  MutableHandle<> toHandle{
2323
0
      runtime, HermesValue::encodeUntrustedNumberValue(to)};
2324
2325
0
  MutableHandle<SymbolID> fromNameTmpStorage{runtime};
2326
0
  MutableHandle<JSObject> fromObj{runtime};
2327
0
  MutableHandle<> fromVal{runtime};
2328
2329
0
  GCScopeMarkerRAII marker{gcScope};
2330
0
  for (; count > 0; marker.flush()) {
2331
    // 17. Repeat, while count > 0
2332
    // a. Let fromKey be ToString(from).
2333
    // b. Let toKey be ToString(to).
2334
2335
    // c. Let fromPresent be HasProperty(O, fromKey).
2336
    // d. ReturnIfAbrupt(fromPresent).
2337
0
    ComputedPropertyDescriptor fromDesc;
2338
0
    if (LLVM_UNLIKELY(
2339
0
            JSObject::getComputedDescriptor(
2340
0
                O,
2341
0
                runtime,
2342
0
                fromHandle,
2343
0
                fromObj,
2344
0
                fromNameTmpStorage,
2345
0
                fromDesc) == ExecutionStatus::EXCEPTION)) {
2346
0
      return ExecutionStatus::EXCEPTION;
2347
0
    }
2348
0
    CallResult<PseudoHandle<>> fromValRes =
2349
0
        JSObject::getComputedPropertyValue_RJS(
2350
0
            O, runtime, fromObj, fromNameTmpStorage, fromDesc, fromHandle);
2351
0
    if (LLVM_UNLIKELY(fromValRes == ExecutionStatus::EXCEPTION)) {
2352
0
      return ExecutionStatus::EXCEPTION;
2353
0
    }
2354
    // e. If fromPresent is true, then
2355
0
    if (LLVM_LIKELY(!(*fromValRes)->isEmpty())) {
2356
      // i. Let fromVal be Get(O, fromKey).
2357
      // ii. ReturnIfAbrupt(fromVal).
2358
0
      fromVal = std::move(*fromValRes);
2359
2360
      // iii. Let setStatus be Set(O, toKey, fromVal, true).
2361
      // iv. ReturnIfAbrupt(setStatus).
2362
0
      if (LLVM_UNLIKELY(
2363
0
              JSObject::putComputed_RJS(
2364
0
                  O,
2365
0
                  runtime,
2366
0
                  toHandle,
2367
0
                  fromVal,
2368
0
                  PropOpFlags().plusThrowOnError()) ==
2369
0
              ExecutionStatus::EXCEPTION)) {
2370
0
        return ExecutionStatus::EXCEPTION;
2371
0
      }
2372
0
    } else {
2373
      // f. Else fromPresent is false,
2374
      // i. Let deleteStatus be DeletePropertyOrThrow(O, toKey).
2375
      // ii. ReturnIfAbrupt(deleteStatus).
2376
0
      if (LLVM_UNLIKELY(
2377
0
              JSObject::deleteComputed(
2378
0
                  O, runtime, toHandle, PropOpFlags().plusThrowOnError()) ==
2379
0
              ExecutionStatus::EXCEPTION)) {
2380
0
        return ExecutionStatus::EXCEPTION;
2381
0
      }
2382
0
    }
2383
2384
    // g. Let from be from + direction.
2385
0
    fromHandle = HermesValue::encodeUntrustedNumberValue(
2386
0
        fromHandle->getNumber() + direction);
2387
    // h. Let to be to + direction.
2388
0
    toHandle = HermesValue::encodeUntrustedNumberValue(
2389
0
        toHandle->getNumber() + direction);
2390
2391
    // i. Let count be count − 1.
2392
0
    --count;
2393
0
  }
2394
  // 18. Return O.
2395
0
  return O.getHermesValue();
2396
0
}
2397
2398
CallResult<HermesValue>
2399
0
arrayPrototypePop(void *, Runtime &runtime, NativeArgs args) {
2400
0
  GCScope gcScope(runtime);
2401
0
  auto res = toObject(runtime, args.getThisHandle());
2402
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
2403
0
    return ExecutionStatus::EXCEPTION;
2404
0
  }
2405
0
  auto O = runtime.makeHandle<JSObject>(res.getValue());
2406
2407
0
  auto propRes = JSObject::getNamed_RJS(
2408
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2409
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2410
0
    return ExecutionStatus::EXCEPTION;
2411
0
  }
2412
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2413
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2414
0
    return ExecutionStatus::EXCEPTION;
2415
0
  }
2416
0
  uint64_t len = *intRes;
2417
2418
0
  if (len == 0) {
2419
0
    if (LLVM_UNLIKELY(
2420
0
            JSObject::putNamed_RJS(
2421
0
                O,
2422
0
                runtime,
2423
0
                Predefined::getSymbolID(Predefined::length),
2424
0
                runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(0)),
2425
0
                PropOpFlags().plusThrowOnError()) ==
2426
0
            ExecutionStatus::EXCEPTION))
2427
0
      return ExecutionStatus::EXCEPTION;
2428
0
    return HermesValue::encodeUndefinedValue();
2429
0
  }
2430
2431
0
  auto idxVal =
2432
0
      runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(len - 1));
2433
0
  if (LLVM_UNLIKELY(
2434
0
          (propRes = JSObject::getComputed_RJS(O, runtime, idxVal)) ==
2435
0
          ExecutionStatus::EXCEPTION)) {
2436
0
    return ExecutionStatus::EXCEPTION;
2437
0
  }
2438
0
  auto element = runtime.makeHandle(std::move(*propRes));
2439
0
  if (LLVM_UNLIKELY(
2440
0
          JSObject::deleteComputed(
2441
0
              O, runtime, idxVal, PropOpFlags().plusThrowOnError()) ==
2442
0
          ExecutionStatus::EXCEPTION)) {
2443
0
    return ExecutionStatus::EXCEPTION;
2444
0
  }
2445
2446
0
  if (LLVM_UNLIKELY(
2447
0
          JSObject::putNamed_RJS(
2448
0
              O,
2449
0
              runtime,
2450
0
              Predefined::getSymbolID(Predefined::length),
2451
0
              runtime.makeHandle(
2452
0
                  HermesValue::encodeUntrustedNumberValue(len - 1)),
2453
0
              PropOpFlags().plusThrowOnError()) == ExecutionStatus::EXCEPTION))
2454
0
    return ExecutionStatus::EXCEPTION;
2455
0
  return element.get();
2456
0
}
2457
2458
CallResult<HermesValue>
2459
0
arrayPrototypeShift(void *, Runtime &runtime, NativeArgs args) {
2460
0
  GCScope gcScope(runtime);
2461
0
  auto objRes = toObject(runtime, args.getThisHandle());
2462
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
2463
0
    return ExecutionStatus::EXCEPTION;
2464
0
  }
2465
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
2466
2467
0
  auto propRes = JSObject::getNamed_RJS(
2468
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2469
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2470
0
    return ExecutionStatus::EXCEPTION;
2471
0
  }
2472
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2473
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2474
0
    return ExecutionStatus::EXCEPTION;
2475
0
  }
2476
0
  uint64_t len = *intRes;
2477
2478
0
  if (len == 0) {
2479
    // Need to set length to 0 per spec.
2480
0
    if (JSObject::putNamed_RJS(
2481
0
            O,
2482
0
            runtime,
2483
0
            Predefined::getSymbolID(Predefined::length),
2484
0
            runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(0)),
2485
0
            PropOpFlags().plusThrowOnError()) == ExecutionStatus::EXCEPTION)
2486
0
      return ExecutionStatus::EXCEPTION;
2487
0
    return HermesValue::encodeUndefinedValue();
2488
0
  }
2489
2490
0
  auto idxVal = runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(0));
2491
0
  if (LLVM_UNLIKELY(
2492
0
          (propRes = JSObject::getComputed_RJS(O, runtime, idxVal)) ==
2493
0
          ExecutionStatus::EXCEPTION)) {
2494
0
    return ExecutionStatus::EXCEPTION;
2495
0
  }
2496
0
  auto first = runtime.makeHandle(std::move(*propRes));
2497
2498
0
  MutableHandle<> from{runtime, HermesValue::encodeUntrustedNumberValue(1)};
2499
0
  MutableHandle<> to{runtime};
2500
2501
0
  MutableHandle<SymbolID> fromNameTmpStorage{runtime};
2502
0
  MutableHandle<JSObject> fromDescObjHandle{runtime};
2503
0
  MutableHandle<> fromVal{runtime};
2504
2505
  // Move every element to the left one slot.
2506
  // TODO: Add a fast path for actual arrays.
2507
0
  while (from->getDouble() < len) {
2508
0
    GCScopeMarkerRAII marker{gcScope};
2509
2510
    // Moving an element from "from" to "from - 1".
2511
0
    to = HermesValue::encodeUntrustedNumberValue(from->getDouble() - 1);
2512
2513
0
    ComputedPropertyDescriptor fromDesc;
2514
0
    JSObject::getComputedPrimitiveDescriptor(
2515
0
        O, runtime, from, fromDescObjHandle, fromNameTmpStorage, fromDesc);
2516
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
2517
0
        O, runtime, fromDescObjHandle, fromNameTmpStorage, fromDesc, from);
2518
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2519
0
      return ExecutionStatus::EXCEPTION;
2520
0
    }
2521
2522
0
    if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
2523
      // fromPresent is true, so read fromVal and set the "to" index.
2524
0
      fromVal = std::move(*propRes);
2525
0
      if (LLVM_UNLIKELY(
2526
0
              JSObject::putComputed_RJS(
2527
0
                  O, runtime, to, fromVal, PropOpFlags().plusThrowOnError()) ==
2528
0
              ExecutionStatus::EXCEPTION)) {
2529
0
        return ExecutionStatus::EXCEPTION;
2530
0
      }
2531
0
    } else {
2532
      // fromVal is not present so move the empty slot to the left.
2533
0
      if (LLVM_UNLIKELY(
2534
0
              JSObject::deleteComputed(
2535
0
                  O, runtime, to, PropOpFlags().plusThrowOnError()) ==
2536
0
              ExecutionStatus::EXCEPTION)) {
2537
0
        return ExecutionStatus::EXCEPTION;
2538
0
      }
2539
0
    }
2540
2541
0
    from = HermesValue::encodeUntrustedNumberValue(from->getDouble() + 1);
2542
0
  }
2543
2544
  // Delete last element of the array.
2545
0
  if (LLVM_UNLIKELY(
2546
0
          JSObject::deleteComputed(
2547
0
              O,
2548
0
              runtime,
2549
0
              runtime.makeHandle(
2550
0
                  HermesValue::encodeUntrustedNumberValue(len - 1)),
2551
0
              PropOpFlags().plusThrowOnError()) ==
2552
0
          ExecutionStatus::EXCEPTION)) {
2553
0
    return ExecutionStatus::EXCEPTION;
2554
0
  }
2555
2556
  // Decrement length.
2557
0
  if (LLVM_UNLIKELY(
2558
0
          JSObject::putNamed_RJS(
2559
0
              O,
2560
0
              runtime,
2561
0
              Predefined::getSymbolID(Predefined::length),
2562
0
              runtime.makeHandle(
2563
0
                  HermesValue::encodeUntrustedNumberValue(len - 1)),
2564
0
              PropOpFlags().plusThrowOnError()) == ExecutionStatus::EXCEPTION))
2565
0
    return ExecutionStatus::EXCEPTION;
2566
0
  return first.get();
2567
0
}
2568
2569
/// Used to help with indexOf and lastIndexOf.
2570
/// \p reverse true if searching in reverse (lastIndexOf), false otherwise.
2571
static inline CallResult<HermesValue>
2572
0
indexOfHelper(Runtime &runtime, NativeArgs args, const bool reverse) {
2573
0
  GCScope gcScope(runtime);
2574
0
  auto objRes = toObject(runtime, args.getThisHandle());
2575
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
2576
0
    return ExecutionStatus::EXCEPTION;
2577
0
  }
2578
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
2579
2580
0
  auto propRes = JSObject::getNamed_RJS(
2581
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2582
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2583
0
    return ExecutionStatus::EXCEPTION;
2584
0
  }
2585
0
  auto lenRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2586
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
2587
0
    return ExecutionStatus::EXCEPTION;
2588
0
  }
2589
0
  double len = *lenRes;
2590
2591
  // Early return before running into any coercions on args.
2592
  // 2. Let len be ? LengthOfArrayLike(O).
2593
  // 3. If len is 0, return -1.
2594
0
  if (len == 0) {
2595
0
    return HermesValue::encodeUntrustedNumberValue(-1);
2596
0
  }
2597
2598
  // Relative index to start the search at.
2599
0
  auto intRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
2600
0
  double n;
2601
0
  if (args.getArgCount() > 1) {
2602
0
    if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2603
0
      return ExecutionStatus::EXCEPTION;
2604
0
    }
2605
0
    n = intRes->getNumber();
2606
0
    if (LLVM_UNLIKELY(n == 0)) {
2607
      // To handle the special case when n is -0, we need to make sure it's 0.
2608
0
      n = 0;
2609
0
    }
2610
0
  } else {
2611
0
    n = !reverse ? 0 : len - 1;
2612
0
  }
2613
2614
  // Actual index to start the search at.
2615
0
  MutableHandle<> k{runtime};
2616
0
  if (!reverse) {
2617
0
    if (n >= 0) {
2618
0
      k = HermesValue::encodeUntrustedNumberValue(n);
2619
0
    } else {
2620
      // If len - abs(n) < 0, set k=0. Otherwise set k = len - abs(n).
2621
0
      k = HermesValue::encodeUntrustedNumberValue(
2622
0
          std::max(len - std::abs(n), 0.0));
2623
0
    }
2624
0
  } else {
2625
0
    if (n >= 0) {
2626
0
      k = HermesValue::encodeUntrustedNumberValue(std::min(n, len - 1));
2627
0
    } else {
2628
0
      k = HermesValue::encodeUntrustedNumberValue(len - std::abs(n));
2629
0
    }
2630
0
  }
2631
2632
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
2633
0
  MutableHandle<JSObject> descObjHandle{runtime};
2634
2635
  // Search for the element.
2636
0
  auto searchElement = args.getArgHandle(0);
2637
0
  auto marker = gcScope.createMarker();
2638
0
  while (true) {
2639
0
    gcScope.flushToMarker(marker);
2640
    // Check that we're not done yet.
2641
0
    if (!reverse) {
2642
0
      if (k->getDouble() >= len) {
2643
0
        break;
2644
0
      }
2645
0
    } else {
2646
0
      if (k->getDouble() < 0) {
2647
0
        break;
2648
0
      }
2649
0
    }
2650
0
    ComputedPropertyDescriptor desc;
2651
0
    JSObject::getComputedPrimitiveDescriptor(
2652
0
        O, runtime, k, descObjHandle, tmpPropNameStorage, desc);
2653
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
2654
0
        O, runtime, descObjHandle, tmpPropNameStorage, desc, k);
2655
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2656
0
      return ExecutionStatus::EXCEPTION;
2657
0
    }
2658
0
    if (!(*propRes)->isEmpty() &&
2659
0
        strictEqualityTest(searchElement.get(), propRes->get())) {
2660
0
      return k.get();
2661
0
    }
2662
    // Update the index based on the direction of the search.
2663
0
    k = HermesValue::encodeUntrustedNumberValue(
2664
0
        k->getDouble() + (reverse ? -1 : 1));
2665
0
  }
2666
2667
  // Not found, return -1.
2668
0
  return HermesValue::encodeUntrustedNumberValue(-1);
2669
0
}
2670
2671
CallResult<HermesValue>
2672
0
arrayPrototypeUnshift(void *, Runtime &runtime, NativeArgs args) {
2673
0
  GCScope gcScope(runtime);
2674
0
  auto objRes = toObject(runtime, args.getThisHandle());
2675
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
2676
0
    return ExecutionStatus::EXCEPTION;
2677
0
  }
2678
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
2679
2680
0
  auto propRes = JSObject::getNamed_RJS(
2681
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2682
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2683
0
    return ExecutionStatus::EXCEPTION;
2684
0
  }
2685
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2686
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2687
0
    return ExecutionStatus::EXCEPTION;
2688
0
  }
2689
0
  uint64_t len = *intRes;
2690
0
  size_t argCount = args.getArgCount();
2691
2692
  // 4. If argCount > 0, then
2693
0
  if (argCount > 0) {
2694
    // If len+ argCount > (2 ^ 53) -1, throw a TypeError exception.
2695
0
    if (LLVM_UNLIKELY(len + argCount >= ((uint64_t)1 << 53) - 1)) {
2696
0
      return runtime.raiseTypeError(
2697
0
          "Array.prototype.unshift result out of space");
2698
0
    }
2699
2700
    // Loop indices.
2701
0
    MutableHandle<> k{runtime, HermesValue::encodeUntrustedNumberValue(len)};
2702
0
    MutableHandle<> j{runtime, HermesValue::encodeUntrustedNumberValue(0)};
2703
2704
    // Indices to copy from/to when shifting.
2705
0
    MutableHandle<> from{runtime};
2706
0
    MutableHandle<> to{runtime};
2707
2708
    // Value that is being copied.
2709
0
    MutableHandle<SymbolID> fromNameTmpStorage{runtime};
2710
0
    MutableHandle<JSObject> fromDescObjHandle{runtime};
2711
0
    MutableHandle<> fromValue{runtime};
2712
2713
    // Move elements to the right by argCount to account for the new elements.
2714
    // TODO: Add a fast path for actual arrays.
2715
0
    auto marker = gcScope.createMarker();
2716
0
    while (k->getDouble() > 0) {
2717
0
      gcScope.flushToMarker(marker);
2718
0
      from = HermesValue::encodeUntrustedNumberValue(k->getDouble() - 1);
2719
0
      to = HermesValue::encodeUntrustedNumberValue(
2720
0
          k->getDouble() + argCount - 1);
2721
2722
0
      ComputedPropertyDescriptor fromDesc;
2723
0
      JSObject::getComputedPrimitiveDescriptor(
2724
0
          O, runtime, from, fromDescObjHandle, fromNameTmpStorage, fromDesc);
2725
0
      CallResult<PseudoHandle<>> propRes =
2726
0
          JSObject::getComputedPropertyValue_RJS(
2727
0
              O,
2728
0
              runtime,
2729
0
              fromDescObjHandle,
2730
0
              fromNameTmpStorage,
2731
0
              fromDesc,
2732
0
              from);
2733
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2734
0
        return ExecutionStatus::EXCEPTION;
2735
0
      }
2736
2737
0
      if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
2738
0
        fromValue = std::move(*propRes);
2739
0
        if (LLVM_UNLIKELY(
2740
0
                JSObject::putComputed_RJS(
2741
0
                    O,
2742
0
                    runtime,
2743
0
                    to,
2744
0
                    fromValue,
2745
0
                    PropOpFlags().plusThrowOnError()) ==
2746
0
                ExecutionStatus::EXCEPTION)) {
2747
0
          return ExecutionStatus::EXCEPTION;
2748
0
        }
2749
0
      } else {
2750
        // Shift the empty slot by deleting at the destination.
2751
0
        if (LLVM_UNLIKELY(
2752
0
                JSObject::deleteComputed(
2753
0
                    O, runtime, to, PropOpFlags().plusThrowOnError()) ==
2754
0
                ExecutionStatus::EXCEPTION)) {
2755
0
          return ExecutionStatus::EXCEPTION;
2756
0
        }
2757
0
      }
2758
0
      k = HermesValue::encodeUntrustedNumberValue(k->getDouble() - 1);
2759
0
    }
2760
2761
    // Put the arguments into the beginning of the array.
2762
0
    for (auto arg : args.handles()) {
2763
0
      if (LLVM_UNLIKELY(
2764
0
              JSObject::putComputed_RJS(
2765
0
                  O, runtime, j, arg, PropOpFlags().plusThrowOnError()) ==
2766
0
              ExecutionStatus::EXCEPTION)) {
2767
0
        return ExecutionStatus::EXCEPTION;
2768
0
      }
2769
0
      gcScope.flushToMarker(marker);
2770
0
      j = HermesValue::encodeUntrustedNumberValue(j->getDouble() + 1);
2771
0
    }
2772
0
  }
2773
2774
  // Increment length by argCount.
2775
0
  auto newLen = HermesValue::encodeUntrustedNumberValue(len + argCount);
2776
0
  if (LLVM_UNLIKELY(
2777
0
          JSObject::putNamed_RJS(
2778
0
              O,
2779
0
              runtime,
2780
0
              Predefined::getSymbolID(Predefined::length),
2781
0
              runtime.makeHandle(newLen),
2782
0
              PropOpFlags().plusThrowOnError()) == ExecutionStatus::EXCEPTION))
2783
0
    return ExecutionStatus::EXCEPTION;
2784
0
  return newLen;
2785
0
}
2786
2787
CallResult<HermesValue>
2788
0
arrayPrototypeIndexOf(void *, Runtime &runtime, NativeArgs args) {
2789
0
  return indexOfHelper(runtime, args, false);
2790
0
}
2791
2792
CallResult<HermesValue>
2793
0
arrayPrototypeLastIndexOf(void *, Runtime &runtime, NativeArgs args) {
2794
0
  return indexOfHelper(runtime, args, true);
2795
0
}
2796
2797
/// Helper function for every/some.
2798
/// \param every true if calling every(), false if calling some().
2799
static inline CallResult<HermesValue>
2800
0
everySomeHelper(Runtime &runtime, NativeArgs args, const bool every) {
2801
0
  GCScope gcScope(runtime);
2802
0
  auto objRes = toObject(runtime, args.getThisHandle());
2803
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
2804
0
    return ExecutionStatus::EXCEPTION;
2805
0
  }
2806
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
2807
2808
0
  auto propRes = JSObject::getNamed_RJS(
2809
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2810
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2811
0
    return ExecutionStatus::EXCEPTION;
2812
0
  }
2813
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2814
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2815
0
    return ExecutionStatus::EXCEPTION;
2816
0
  }
2817
0
  uint64_t len = *intRes;
2818
2819
0
  auto callbackFn = args.dyncastArg<Callable>(0);
2820
0
  if (!callbackFn) {
2821
0
    return runtime.raiseTypeError(
2822
0
        "Array.prototype.every() requires a callable argument");
2823
0
  }
2824
2825
  // Index to check the callback on.
2826
0
  MutableHandle<> k{runtime, HermesValue::encodeUntrustedNumberValue(0)};
2827
2828
  // Value at index k;
2829
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
2830
0
  MutableHandle<JSObject> descObjHandle{runtime};
2831
0
  MutableHandle<> kValue{runtime};
2832
2833
  // Loop through and run the callback.
2834
0
  auto marker = gcScope.createMarker();
2835
0
  while (k->getDouble() < len) {
2836
0
    gcScope.flushToMarker(marker);
2837
2838
0
    ComputedPropertyDescriptor desc;
2839
0
    JSObject::getComputedPrimitiveDescriptor(
2840
0
        O, runtime, k, descObjHandle, tmpPropNameStorage, desc);
2841
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
2842
0
        O, runtime, descObjHandle, tmpPropNameStorage, desc, k);
2843
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2844
0
      return ExecutionStatus::EXCEPTION;
2845
0
    }
2846
0
    if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
2847
      // kPresent is true, call the callback on the kth element.
2848
0
      kValue = std::move(*propRes);
2849
0
      auto callRes = Callable::executeCall3(
2850
0
          callbackFn,
2851
0
          runtime,
2852
0
          args.getArgHandle(1),
2853
0
          kValue.get(),
2854
0
          k.get(),
2855
0
          O.getHermesValue());
2856
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
2857
0
        return ExecutionStatus::EXCEPTION;
2858
0
      }
2859
0
      auto testResult = std::move(*callRes);
2860
0
      if (every) {
2861
        // Done if one is false.
2862
0
        if (!toBoolean(testResult.get())) {
2863
0
          return HermesValue::encodeBoolValue(false);
2864
0
        }
2865
0
      } else {
2866
        // Done if one is true.
2867
0
        if (toBoolean(testResult.get())) {
2868
0
          return HermesValue::encodeBoolValue(true);
2869
0
        }
2870
0
      }
2871
0
    }
2872
2873
0
    k = HermesValue::encodeUntrustedNumberValue(k->getDouble() + 1);
2874
0
  }
2875
2876
  // If we're looking for every, then we finished without returning true.
2877
  // If we're looking for some, then we finished without returning false.
2878
0
  return HermesValue::encodeBoolValue(every);
2879
0
}
2880
2881
CallResult<HermesValue>
2882
0
arrayPrototypeEvery(void *, Runtime &runtime, NativeArgs args) {
2883
0
  return everySomeHelper(runtime, args, true);
2884
0
}
2885
2886
CallResult<HermesValue>
2887
0
arrayPrototypeSome(void *, Runtime &runtime, NativeArgs args) {
2888
0
  return everySomeHelper(runtime, args, false);
2889
0
}
2890
2891
CallResult<HermesValue>
2892
0
arrayPrototypeMap(void *, Runtime &runtime, NativeArgs args) {
2893
0
  GCScope gcScope(runtime);
2894
0
  auto objRes = toObject(runtime, args.getThisHandle());
2895
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
2896
0
    return ExecutionStatus::EXCEPTION;
2897
0
  }
2898
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
2899
2900
0
  auto propRes = JSObject::getNamed_RJS(
2901
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2902
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2903
0
    return ExecutionStatus::EXCEPTION;
2904
0
  }
2905
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2906
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2907
0
    return ExecutionStatus::EXCEPTION;
2908
0
  }
2909
0
  uint64_t len = *intRes;
2910
2911
0
  auto callbackFn = args.dyncastArg<Callable>(0);
2912
0
  if (!callbackFn) {
2913
0
    return runtime.raiseTypeError(
2914
0
        "Array.prototype.map() requires a callable argument");
2915
0
  }
2916
2917
  // Resultant array.
2918
0
  if (LLVM_UNLIKELY(len > JSArray::StorageType::maxElements())) {
2919
0
    return runtime.raiseRangeError("Out of memory for array elements.");
2920
0
  }
2921
0
  auto arrRes = JSArray::create(runtime, len, len);
2922
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
2923
0
    return ExecutionStatus::EXCEPTION;
2924
0
  }
2925
0
  auto A = *arrRes;
2926
2927
  // Current index to execute callback on.
2928
0
  MutableHandle<> k{runtime, HermesValue::encodeUntrustedNumberValue(0)};
2929
2930
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
2931
0
  MutableHandle<JSObject> descObjHandle{runtime};
2932
2933
  // Main loop to execute callback and store the results in A.
2934
  // TODO: Implement a fast path for actual arrays.
2935
0
  auto marker = gcScope.createMarker();
2936
0
  while (k->getDouble() < len) {
2937
0
    gcScope.flushToMarker(marker);
2938
2939
0
    ComputedPropertyDescriptor desc;
2940
0
    JSObject::getComputedPrimitiveDescriptor(
2941
0
        O, runtime, k, descObjHandle, tmpPropNameStorage, desc);
2942
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
2943
0
        O, runtime, descObjHandle, tmpPropNameStorage, desc, k);
2944
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2945
0
      return ExecutionStatus::EXCEPTION;
2946
0
    }
2947
0
    if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
2948
      // kPresent is true, execute callback and store result in A[k].
2949
0
      auto kValue = std::move(*propRes);
2950
0
      auto callRes = Callable::executeCall3(
2951
0
          callbackFn,
2952
0
          runtime,
2953
0
          args.getArgHandle(1),
2954
0
          kValue.get(),
2955
0
          k.get(),
2956
0
          O.getHermesValue());
2957
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
2958
0
        return ExecutionStatus::EXCEPTION;
2959
0
      }
2960
0
      JSArray::setElementAt(
2961
0
          A, runtime, k->getDouble(), runtime.makeHandle(std::move(*callRes)));
2962
0
    }
2963
2964
0
    k = HermesValue::encodeUntrustedNumberValue(k->getDouble() + 1);
2965
0
  }
2966
2967
0
  return A.getHermesValue();
2968
0
}
2969
2970
CallResult<HermesValue>
2971
0
arrayPrototypeFilter(void *, Runtime &runtime, NativeArgs args) {
2972
0
  GCScope gcScope(runtime);
2973
0
  auto objRes = toObject(runtime, args.getThisHandle());
2974
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
2975
0
    return ExecutionStatus::EXCEPTION;
2976
0
  }
2977
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
2978
2979
0
  auto propRes = JSObject::getNamed_RJS(
2980
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
2981
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2982
0
    return ExecutionStatus::EXCEPTION;
2983
0
  }
2984
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
2985
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2986
0
    return ExecutionStatus::EXCEPTION;
2987
0
  }
2988
0
  uint64_t len = *intRes;
2989
2990
0
  auto callbackFn = args.dyncastArg<Callable>(0);
2991
0
  if (!callbackFn) {
2992
0
    return runtime.raiseTypeError(
2993
0
        "Array.prototype.filter() requires a callable argument");
2994
0
  }
2995
2996
0
  if (LLVM_UNLIKELY(len > JSArray::StorageType::maxElements())) {
2997
0
    return runtime.raiseRangeError("Out of memory for array elements.");
2998
0
  }
2999
0
  auto arrRes = JSArray::create(runtime, len, 0);
3000
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
3001
0
    return ExecutionStatus::EXCEPTION;
3002
0
  }
3003
0
  auto A = *arrRes;
3004
3005
  // Index in the original array.
3006
0
  MutableHandle<> k{runtime, HermesValue::encodeUntrustedNumberValue(0)};
3007
  // Index to copy to in the new array.
3008
0
  uint32_t to = 0;
3009
3010
  // Value at index k.
3011
0
  MutableHandle<SymbolID> tmpPropNameStorage{runtime};
3012
0
  MutableHandle<JSObject> descObjHandle{runtime};
3013
0
  MutableHandle<> kValue{runtime};
3014
3015
0
  auto marker = gcScope.createMarker();
3016
0
  while (k->getDouble() < len) {
3017
0
    gcScope.flushToMarker(marker);
3018
3019
0
    ComputedPropertyDescriptor desc;
3020
0
    JSObject::getComputedPrimitiveDescriptor(
3021
0
        O, runtime, k, descObjHandle, tmpPropNameStorage, desc);
3022
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
3023
0
        O, runtime, descObjHandle, tmpPropNameStorage, desc, k);
3024
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3025
0
      return ExecutionStatus::EXCEPTION;
3026
0
    }
3027
0
    if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
3028
0
      kValue = std::move(*propRes);
3029
      // Call the callback.
3030
0
      auto callRes = Callable::executeCall3(
3031
0
          callbackFn,
3032
0
          runtime,
3033
0
          args.getArgHandle(1),
3034
0
          kValue.get(),
3035
0
          k.get(),
3036
0
          O.getHermesValue());
3037
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
3038
0
        return ExecutionStatus::EXCEPTION;
3039
0
      }
3040
0
      if (toBoolean(callRes->get())) {
3041
        // Add the element to the array if it passes the callback.
3042
0
        JSArray::setElementAt(A, runtime, to, kValue);
3043
0
        ++to;
3044
0
      }
3045
0
    }
3046
3047
0
    k = HermesValue::encodeUntrustedNumberValue(k->getDouble() + 1);
3048
0
  }
3049
3050
0
  if (LLVM_UNLIKELY(
3051
0
          JSArray::setLengthProperty(A, runtime, to) ==
3052
0
          ExecutionStatus::EXCEPTION))
3053
0
    return ExecutionStatus::EXCEPTION;
3054
0
  return A.getHermesValue();
3055
0
}
3056
3057
CallResult<HermesValue>
3058
0
arrayPrototypeFill(void *, Runtime &runtime, NativeArgs args) {
3059
0
  GCScope gcScope(runtime);
3060
0
  auto objRes = toObject(runtime, args.getThisHandle());
3061
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
3062
0
    return ExecutionStatus::EXCEPTION;
3063
0
  }
3064
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
3065
  // Get the length.
3066
0
  auto propRes = JSObject::getNamed_RJS(
3067
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
3068
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3069
0
    return ExecutionStatus::EXCEPTION;
3070
0
  }
3071
0
  auto lenRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
3072
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
3073
0
    return ExecutionStatus::EXCEPTION;
3074
0
  }
3075
0
  double len = *lenRes;
3076
  // Get the value to be filled.
3077
0
  MutableHandle<> value(runtime, args.getArg(0));
3078
  // Get the relative start and end.
3079
0
  auto intRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
3080
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
3081
0
    return ExecutionStatus::EXCEPTION;
3082
0
  }
3083
0
  double relativeStart = intRes->getNumber();
3084
  // Index to start the deletion/insertion at.
3085
0
  double actualStart = relativeStart < 0 ? std::max(len + relativeStart, 0.0)
3086
0
                                         : std::min(relativeStart, len);
3087
0
  double relativeEnd;
3088
0
  if (args.getArg(2).isUndefined()) {
3089
0
    relativeEnd = len;
3090
0
  } else {
3091
0
    if (LLVM_UNLIKELY(
3092
0
            (intRes = toIntegerOrInfinity(runtime, args.getArgHandle(2))) ==
3093
0
            ExecutionStatus::EXCEPTION)) {
3094
0
      return ExecutionStatus::EXCEPTION;
3095
0
    }
3096
0
    relativeEnd = intRes->getNumber();
3097
0
  }
3098
  // Actual end index.
3099
0
  double actualEnd = relativeEnd < 0 ? std::max(len + relativeEnd, 0.0)
3100
0
                                     : std::min(relativeEnd, len);
3101
0
  MutableHandle<> k(
3102
0
      runtime, HermesValue::encodeUntrustedNumberValue(actualStart));
3103
0
  auto marker = gcScope.createMarker();
3104
0
  while (k->getDouble() < actualEnd) {
3105
0
    if (LLVM_UNLIKELY(
3106
0
            JSObject::putComputed_RJS(
3107
0
                O, runtime, k, value, PropOpFlags().plusThrowOnError()) ==
3108
0
            ExecutionStatus::EXCEPTION)) {
3109
0
      return ExecutionStatus::EXCEPTION;
3110
0
    }
3111
0
    k.set(HermesValue::encodeUntrustedNumberValue(k->getDouble() + 1));
3112
0
    gcScope.flushToMarker(marker);
3113
0
  }
3114
0
  return O.getHermesValue();
3115
0
}
3116
3117
static CallResult<HermesValue>
3118
0
findHelper(void *ctx, bool reverse, Runtime &runtime, NativeArgs args) {
3119
0
  GCScope gcScope{runtime};
3120
0
  bool findIndex = ctx != nullptr;
3121
0
  auto objRes = toObject(runtime, args.getThisHandle());
3122
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
3123
0
    return ExecutionStatus::EXCEPTION;
3124
0
  }
3125
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
3126
3127
  // Get the length.
3128
0
  auto propRes = JSObject::getNamed_RJS(
3129
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
3130
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3131
0
    return ExecutionStatus::EXCEPTION;
3132
0
  }
3133
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
3134
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
3135
0
    return ExecutionStatus::EXCEPTION;
3136
0
  }
3137
0
  double len = *intRes;
3138
3139
0
  auto predicate = args.dyncastArg<Callable>(0);
3140
0
  if (!predicate) {
3141
0
    return runtime.raiseTypeError("Find argument must be a function");
3142
0
  }
3143
3144
  // "this" argument to the callback function.
3145
0
  auto T = args.getArgHandle(1);
3146
0
  MutableHandle<> kHandle{runtime};
3147
0
  MutableHandle<> kValue{runtime};
3148
0
  auto marker = gcScope.createMarker();
3149
0
  for (size_t i = 0; i < len; ++i) {
3150
0
    kHandle =
3151
0
        HermesValue::encodeUntrustedNumberValue(reverse ? (len - i - 1) : i);
3152
0
    gcScope.flushToMarker(marker);
3153
0
    if (LLVM_UNLIKELY(
3154
0
            (propRes = JSObject::getComputed_RJS(O, runtime, kHandle)) ==
3155
0
            ExecutionStatus::EXCEPTION)) {
3156
0
      return ExecutionStatus::EXCEPTION;
3157
0
    }
3158
0
    kValue = std::move(*propRes);
3159
0
    auto callRes = Callable::executeCall3(
3160
0
        predicate,
3161
0
        runtime,
3162
0
        T,
3163
0
        kValue.getHermesValue(),
3164
0
        kHandle.getHermesValue(),
3165
0
        O.getHermesValue());
3166
0
    if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
3167
0
      return ExecutionStatus::EXCEPTION;
3168
0
    }
3169
0
    bool testResult = toBoolean(callRes->get());
3170
0
    if (testResult) {
3171
      // If this is index find variant, then return the index k.
3172
      // Else, return the value at the index k.
3173
0
      return findIndex ? kHandle.getHermesValue() : kValue.getHermesValue();
3174
0
    }
3175
0
  }
3176
3177
  // Failure case for Array.prototype.findIndex is -1.
3178
  // Failure case for Array.prototype.find is undefined.
3179
  // The last variants share the same failure case values.
3180
0
  return findIndex ? HermesValue::encodeUntrustedNumberValue(-1)
3181
0
                   : HermesValue::encodeUndefinedValue();
3182
0
}
3183
3184
CallResult<HermesValue>
3185
0
arrayPrototypeFind(void *ctx, Runtime &runtime, NativeArgs args) {
3186
0
  return findHelper(ctx, false, runtime, args);
3187
0
}
3188
3189
CallResult<HermesValue>
3190
0
arrayPrototypeFindLast(void *ctx, Runtime &runtime, NativeArgs args) {
3191
0
  return findHelper(ctx, true, runtime, args);
3192
0
}
3193
3194
/// Helper for reduce and reduceRight.
3195
/// \param reverse set to true to reduceRight, false to reduce from the left.
3196
static inline CallResult<HermesValue>
3197
0
reduceHelper(Runtime &runtime, NativeArgs args, const bool reverse) {
3198
0
  GCScope gcScope(runtime);
3199
0
  auto objRes = toObject(runtime, args.getThisHandle());
3200
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
3201
0
    return ExecutionStatus::EXCEPTION;
3202
0
  }
3203
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
3204
3205
0
  auto propRes = JSObject::getNamed_RJS(
3206
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
3207
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3208
0
    return ExecutionStatus::EXCEPTION;
3209
0
  }
3210
0
  auto intRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
3211
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
3212
0
    return ExecutionStatus::EXCEPTION;
3213
0
  }
3214
0
  double len = *intRes;
3215
3216
0
  size_t argCount = args.getArgCount();
3217
3218
0
  auto callbackFn = args.dyncastArg<Callable>(0);
3219
0
  if (!callbackFn) {
3220
0
    return runtime.raiseTypeError(
3221
0
        "Array.prototype.reduce() requires a callable argument");
3222
0
  }
3223
3224
  // Can't reduce an empty array without an initial value.
3225
0
  if (len == 0 && argCount < 2) {
3226
0
    return runtime.raiseTypeError(
3227
0
        "Array.prototype.reduce() requires an initial value with empty array");
3228
0
  }
3229
3230
  // Current index in the reduction iteration.
3231
0
  MutableHandle<> k{
3232
0
      runtime, HermesValue::encodeUntrustedNumberValue(reverse ? len - 1 : 0)};
3233
0
  MutableHandle<SymbolID> kNameTmpStorage{runtime};
3234
0
  MutableHandle<JSObject> kDescObjHandle{runtime};
3235
3236
0
  MutableHandle<> accumulator{runtime};
3237
3238
0
  auto marker = gcScope.createMarker();
3239
3240
  // How much to increment k by each iteration of a loop.
3241
0
  double increment = reverse ? -1 : 1;
3242
3243
  // Initialize the accumulator to either the intialValue arg or the first value
3244
  // of the array.
3245
0
  if (argCount >= 2) {
3246
0
    accumulator = args.getArg(1);
3247
0
  } else {
3248
0
    bool kPresent = false;
3249
0
    while (!kPresent) {
3250
0
      gcScope.flushToMarker(marker);
3251
0
      if (!reverse) {
3252
0
        if (k->getDouble() >= len) {
3253
0
          break;
3254
0
        }
3255
0
      } else {
3256
0
        if (k->getDouble() < 0) {
3257
0
          break;
3258
0
        }
3259
0
      }
3260
0
      ComputedPropertyDescriptor kDesc;
3261
0
      JSObject::getComputedPrimitiveDescriptor(
3262
0
          O, runtime, k, kDescObjHandle, kNameTmpStorage, kDesc);
3263
0
      CallResult<PseudoHandle<>> propRes =
3264
0
          JSObject::getComputedPropertyValue_RJS(
3265
0
              O, runtime, kDescObjHandle, kNameTmpStorage, kDesc, k);
3266
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3267
0
        return ExecutionStatus::EXCEPTION;
3268
0
      }
3269
0
      if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
3270
0
        kPresent = true;
3271
0
        accumulator = std::move(*propRes);
3272
0
      }
3273
0
      k = HermesValue::encodeUntrustedNumberValue(k->getDouble() + increment);
3274
0
    }
3275
0
    if (!kPresent) {
3276
0
      return runtime.raiseTypeError(
3277
0
          "Array.prototype.reduce() requires an intial value with empty array");
3278
0
    }
3279
0
  }
3280
3281
  // Perform the reduce.
3282
0
  while (true) {
3283
0
    gcScope.flushToMarker(marker);
3284
0
    if (!reverse) {
3285
0
      if (k->getDouble() >= len) {
3286
0
        break;
3287
0
      }
3288
0
    } else {
3289
0
      if (k->getDouble() < 0) {
3290
0
        break;
3291
0
      }
3292
0
    }
3293
3294
0
    ComputedPropertyDescriptor kDesc;
3295
0
    JSObject::getComputedPrimitiveDescriptor(
3296
0
        O, runtime, k, kDescObjHandle, kNameTmpStorage, kDesc);
3297
0
    CallResult<PseudoHandle<>> propRes = JSObject::getComputedPropertyValue_RJS(
3298
0
        O, runtime, kDescObjHandle, kNameTmpStorage, kDesc, k);
3299
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3300
0
      return ExecutionStatus::EXCEPTION;
3301
0
    }
3302
0
    if (LLVM_LIKELY(!(*propRes)->isEmpty())) {
3303
      // kPresent is true, run the accumulation step.
3304
0
      auto kValue = std::move(*propRes);
3305
0
      auto callRes = Callable::executeCall4(
3306
0
          callbackFn,
3307
0
          runtime,
3308
0
          Runtime::getUndefinedValue(),
3309
0
          accumulator.get(),
3310
0
          kValue.get(),
3311
0
          k.get(),
3312
0
          O.getHermesValue());
3313
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
3314
0
        return ExecutionStatus::EXCEPTION;
3315
0
      }
3316
0
      accumulator = std::move(*callRes);
3317
0
    }
3318
0
    k = HermesValue::encodeUntrustedNumberValue(k->getDouble() + increment);
3319
0
  }
3320
3321
0
  return accumulator.get();
3322
0
}
3323
3324
CallResult<HermesValue>
3325
0
arrayPrototypeReduce(void *, Runtime &runtime, NativeArgs args) {
3326
0
  return reduceHelper(runtime, args, false);
3327
0
}
3328
3329
CallResult<HermesValue>
3330
0
arrayPrototypeReduceRight(void *, Runtime &runtime, NativeArgs args) {
3331
0
  return reduceHelper(runtime, args, true);
3332
0
}
3333
3334
/// ES10.0 22.1.3.23.
3335
CallResult<HermesValue>
3336
0
arrayPrototypeReverse(void *, Runtime &runtime, NativeArgs args) {
3337
0
  GCScope gcScope(runtime);
3338
0
  auto objRes = toObject(runtime, args.getThisHandle());
3339
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
3340
0
    return ExecutionStatus::EXCEPTION;
3341
0
  }
3342
0
  auto O = runtime.makeHandle<JSObject>(objRes.getValue());
3343
3344
0
  MutableHandle<> lower{runtime, HermesValue::encodeUntrustedNumberValue(0)};
3345
0
  MutableHandle<> upper{runtime};
3346
3347
  // The values at the lower and upper indices.
3348
0
  MutableHandle<> lowerValue{runtime};
3349
0
  MutableHandle<> upperValue{runtime};
3350
3351
0
  auto marker = gcScope.createMarker();
3352
3353
0
  auto propRes = JSObject::getNamed_RJS(
3354
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
3355
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3356
0
    return ExecutionStatus::EXCEPTION;
3357
0
  }
3358
0
  auto lenRes = toLengthU64(runtime, runtime.makeHandle(std::move(*propRes)));
3359
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
3360
0
    return ExecutionStatus::EXCEPTION;
3361
0
  }
3362
0
  uint64_t len = *lenRes;
3363
3364
  // Indices used in the reversal process.
3365
0
  uint64_t middle = len / 2;
3366
3367
0
  while (lower->getDouble() != middle) {
3368
0
    gcScope.flushToMarker(marker);
3369
0
    upper =
3370
0
        HermesValue::encodeUntrustedNumberValue(len - lower->getNumber() - 1);
3371
3372
0
    CallResult<bool> lowerExistsRes = JSObject::hasComputed(O, runtime, lower);
3373
0
    if (LLVM_UNLIKELY(lowerExistsRes == ExecutionStatus::EXCEPTION)) {
3374
0
      return ExecutionStatus::EXCEPTION;
3375
0
    }
3376
0
    if (*lowerExistsRes) {
3377
0
      CallResult<PseudoHandle<>> lowerValueRes =
3378
0
          JSObject::getComputed_RJS(O, runtime, lower);
3379
0
      if (LLVM_UNLIKELY(lowerValueRes == ExecutionStatus::EXCEPTION)) {
3380
0
        return ExecutionStatus::EXCEPTION;
3381
0
      }
3382
0
      lowerValue = std::move(*lowerValueRes);
3383
0
      gcScope.flushToMarker(marker);
3384
0
    }
3385
3386
0
    CallResult<bool> upperExistsRes = JSObject::hasComputed(O, runtime, upper);
3387
0
    if (LLVM_UNLIKELY(upperExistsRes == ExecutionStatus::EXCEPTION)) {
3388
0
      return ExecutionStatus::EXCEPTION;
3389
0
    }
3390
0
    if (*upperExistsRes) {
3391
0
      CallResult<PseudoHandle<>> upperValueRes =
3392
0
          JSObject::getComputed_RJS(O, runtime, upper);
3393
0
      if (LLVM_UNLIKELY(upperValueRes == ExecutionStatus::EXCEPTION)) {
3394
0
        return ExecutionStatus::EXCEPTION;
3395
0
      }
3396
0
      upperValue = std::move(*upperValueRes);
3397
0
      gcScope.flushToMarker(marker);
3398
0
    }
3399
3400
    // Handle cases in which lower/upper do/don't exist.
3401
0
    if (*lowerExistsRes && *upperExistsRes) {
3402
0
      if (LLVM_UNLIKELY(
3403
0
              JSObject::putComputed_RJS(
3404
0
                  O,
3405
0
                  runtime,
3406
0
                  lower,
3407
0
                  upperValue,
3408
0
                  PropOpFlags().plusThrowOnError()) ==
3409
0
              ExecutionStatus::EXCEPTION)) {
3410
0
        return ExecutionStatus::EXCEPTION;
3411
0
      }
3412
0
      if (LLVM_UNLIKELY(
3413
0
              JSObject::putComputed_RJS(
3414
0
                  O,
3415
0
                  runtime,
3416
0
                  upper,
3417
0
                  lowerValue,
3418
0
                  PropOpFlags().plusThrowOnError()) ==
3419
0
              ExecutionStatus::EXCEPTION)) {
3420
0
        return ExecutionStatus::EXCEPTION;
3421
0
      }
3422
0
    } else if (*upperExistsRes) {
3423
0
      if (LLVM_UNLIKELY(
3424
0
              JSObject::putComputed_RJS(
3425
0
                  O,
3426
0
                  runtime,
3427
0
                  lower,
3428
0
                  upperValue,
3429
0
                  PropOpFlags().plusThrowOnError()) ==
3430
0
              ExecutionStatus::EXCEPTION)) {
3431
0
        return ExecutionStatus::EXCEPTION;
3432
0
      }
3433
0
      if (LLVM_UNLIKELY(
3434
0
              JSObject::deleteComputed(
3435
0
                  O, runtime, upper, PropOpFlags().plusThrowOnError()) ==
3436
0
              ExecutionStatus::EXCEPTION)) {
3437
0
        return ExecutionStatus::EXCEPTION;
3438
0
      }
3439
0
    } else if (*lowerExistsRes) {
3440
0
      if (LLVM_UNLIKELY(
3441
0
              JSObject::deleteComputed(
3442
0
                  O, runtime, lower, PropOpFlags().plusThrowOnError()) ==
3443
0
              ExecutionStatus::EXCEPTION)) {
3444
0
        return ExecutionStatus::EXCEPTION;
3445
0
      }
3446
0
      if (LLVM_UNLIKELY(
3447
0
              JSObject::putComputed_RJS(
3448
0
                  O,
3449
0
                  runtime,
3450
0
                  upper,
3451
0
                  lowerValue,
3452
0
                  PropOpFlags().plusThrowOnError()) ==
3453
0
              ExecutionStatus::EXCEPTION)) {
3454
0
        return ExecutionStatus::EXCEPTION;
3455
0
      }
3456
0
    }
3457
3458
0
    lower = HermesValue::encodeUntrustedNumberValue(lower->getDouble() + 1);
3459
0
  }
3460
3461
0
  return O.getHermesValue();
3462
0
}
3463
3464
CallResult<HermesValue>
3465
0
arrayPrototypeIncludes(void *, Runtime &runtime, NativeArgs args) {
3466
0
  GCScope gcScope{runtime};
3467
3468
  // 1. Let O be ? ToObject(this value).
3469
0
  auto oRes = toObject(runtime, args.getThisHandle());
3470
0
  if (LLVM_UNLIKELY(oRes == ExecutionStatus::EXCEPTION)) {
3471
0
    return ExecutionStatus::EXCEPTION;
3472
0
  }
3473
0
  auto O = runtime.makeHandle<JSObject>(*oRes);
3474
3475
  // 2. Let len be ? ToLength(? Get(O, "length")).
3476
0
  auto lenPropRes = JSObject::getNamed_RJS(
3477
0
      O, runtime, Predefined::getSymbolID(Predefined::length));
3478
0
  if (LLVM_UNLIKELY(lenPropRes == ExecutionStatus::EXCEPTION)) {
3479
0
    return ExecutionStatus::EXCEPTION;
3480
0
  }
3481
0
  auto lenRes =
3482
0
      toLengthU64(runtime, runtime.makeHandle(std::move(*lenPropRes)));
3483
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
3484
0
    return ExecutionStatus::EXCEPTION;
3485
0
  }
3486
0
  double len = *lenRes;
3487
3488
  // 3. If len is 0, return false.
3489
0
  if (len == 0) {
3490
0
    return HermesValue::encodeBoolValue(false);
3491
0
  }
3492
3493
  // 4. Let n be ? ToIntegerOrInfinity(fromIndex).
3494
  // (If fromIndex is undefined, this step produces the value 0.)
3495
0
  auto nRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
3496
0
  if (LLVM_UNLIKELY(nRes == ExecutionStatus::EXCEPTION)) {
3497
0
    return ExecutionStatus::EXCEPTION;
3498
0
  }
3499
  // Use double here, because ToInteger may return Infinity.
3500
0
  double n = nRes->getNumber();
3501
3502
0
  double k;
3503
0
  if (n >= 0) {
3504
    // 5. If n ≥ 0, then
3505
    // 5a. Let k be n.
3506
0
    k = n;
3507
0
  } else {
3508
    // 6. Else n < 0,
3509
    // 6a. Let k be len + n.
3510
0
    k = len + n;
3511
    // 6b. If k < 0, let k be 0.
3512
0
    if (k < 0) {
3513
0
      k = 0;
3514
0
    }
3515
0
  }
3516
3517
0
  MutableHandle<> kHandle{runtime};
3518
3519
  // 7. Repeat, while k < len
3520
0
  auto marker = gcScope.createMarker();
3521
0
  while (k < len) {
3522
0
    gcScope.flushToMarker(marker);
3523
3524
    // 7a. Let elementK be the result of ? Get(O, ! ToString(k)).
3525
0
    kHandle = HermesValue::encodeUntrustedNumberValue(k);
3526
0
    auto elementKRes = JSObject::getComputed_RJS(O, runtime, kHandle);
3527
0
    if (LLVM_UNLIKELY(elementKRes == ExecutionStatus::EXCEPTION)) {
3528
0
      return ExecutionStatus::EXCEPTION;
3529
0
    }
3530
3531
    // 7b. If SameValueZero(searchElement, elementK) is true, return true.
3532
0
    if (isSameValueZero(args.getArg(0), elementKRes->get())) {
3533
0
      return HermesValue::encodeBoolValue(true);
3534
0
    }
3535
3536
    // 7c. Increase k by 1.
3537
0
    ++k;
3538
0
  }
3539
3540
  // 8. Return false.
3541
0
  return HermesValue::encodeBoolValue(false);
3542
0
}
3543
3544
/// ES14.0 23.1.3.33
3545
CallResult<HermesValue>
3546
0
arrayPrototypeToReversed(void *, Runtime &runtime, NativeArgs args) {
3547
0
  GCScope gcScope{runtime};
3548
3549
  // 1. Let O be ? ToObject(this value).
3550
0
  auto oRes = toObject(runtime, args.getThisHandle());
3551
0
  if (LLVM_UNLIKELY(oRes == ExecutionStatus::EXCEPTION)) {
3552
0
    return ExecutionStatus::EXCEPTION;
3553
0
  }
3554
0
  auto O = runtime.makeHandle<JSObject>(*oRes);
3555
3556
  // 2. Let len be ? LengthOfArrayLike(O).
3557
0
  Handle<JSArray> jsArr = Handle<JSArray>::dyn_vmcast(O);
3558
0
  auto lenRes = lengthOfArrayLike(runtime, O, jsArr);
3559
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
3560
0
    return ExecutionStatus::EXCEPTION;
3561
0
  }
3562
0
  auto len = lenRes.getValue();
3563
3564
  // 3. Let A be ArrayCreate(len).
3565
0
  uint32_t len32 = truncateToUInt32(len);
3566
0
  if (LLVM_UNLIKELY(len32 != len)) {
3567
0
    return runtime.raiseRangeError("invalid array length");
3568
0
  }
3569
0
  auto ARes = JSArray::create(runtime, len32, len32);
3570
0
  if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) {
3571
0
    return ExecutionStatus::EXCEPTION;
3572
0
  }
3573
0
  auto A = ARes.getValue();
3574
3575
  // 4. Let k be 0.
3576
0
  double k = 0;
3577
3578
0
  MutableHandle<> kHandle{runtime};
3579
0
  MutableHandle<> fromHandle{runtime};
3580
0
  MutableHandle<> fromValueHandle{runtime};
3581
3582
0
  auto marker = gcScope.createMarker();
3583
  // 5. Repeat, while k < len,
3584
0
  while (k < len) {
3585
0
    gcScope.flushToMarker(marker);
3586
3587
0
    double from = len - k - 1;
3588
    // 5a. Let from be ! ToString(𝔽(len - k - 1)).
3589
0
    fromHandle = HermesValue::encodeUntrustedNumberValue(from);
3590
3591
    // 5b. Let Pk be ! ToString(𝔽(k)).
3592
0
    kHandle = HermesValue::encodeTrustedNumberValue(k);
3593
3594
    // 5c. Let fromValue be ? Get(O, from).
3595
0
    if (LLVM_LIKELY(jsArr)) {
3596
0
      const SmallHermesValue elm = jsArr->at(runtime, from);
3597
      // If the element is not empty, we can return it directly here.
3598
      // Otherwise, we must proceed to the slow path.
3599
0
      if (!elm.isEmpty()) {
3600
0
        fromValueHandle = elm.unboxToHV(runtime);
3601
0
      }
3602
0
    }
3603
    // Slow path
3604
0
    else {
3605
0
      CallResult<PseudoHandle<>> propRes =
3606
0
          JSObject::getComputed_RJS(O, runtime, fromHandle);
3607
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3608
0
        return ExecutionStatus::EXCEPTION;
3609
0
      }
3610
0
      fromValueHandle = propRes->getHermesValue();
3611
0
    }
3612
3613
    // 5d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
3614
0
    if (LLVM_UNLIKELY(
3615
0
            JSObject::defineOwnComputedPrimitive(
3616
0
                A,
3617
0
                runtime,
3618
0
                kHandle,
3619
0
                DefinePropertyFlags::getDefaultNewPropertyFlags(),
3620
0
                fromValueHandle,
3621
0
                PropOpFlags().plusThrowOnError()) ==
3622
0
            ExecutionStatus::EXCEPTION)) {
3623
0
      return ExecutionStatus::EXCEPTION;
3624
0
    }
3625
3626
    // 5e. Set k to k + 1.
3627
0
    ++k;
3628
0
  }
3629
3630
0
  return A.getHermesValue();
3631
0
}
3632
3633
/// Copies \p count elements from \p from (or \p fromArr if is simple array)
3634
static inline CallResult<double> arrayCopyHelper(
3635
    Runtime &runtime,
3636
    GCScope &gcScope,
3637
    Handle<JSObject> from,
3638
    Handle<JSArray> fromArr,
3639
    uint32_t fromStartIndex,
3640
    Handle<JSArray> to,
3641
    uint32_t toStartIndex,
3642
0
    uint32_t count) {
3643
0
  MutableHandle<> fromIndexHandle{runtime};
3644
0
  MutableHandle<> toIndexHandle{runtime};
3645
0
  MutableHandle<> fromValueHandle{runtime};
3646
3647
0
  auto marker = gcScope.createMarker();
3648
0
  double i = 0;
3649
3650
0
  while (i < count) {
3651
0
    gcScope.flushToMarker(marker);
3652
0
    auto fromIndex = fromStartIndex + i;
3653
3654
0
    if (LLVM_LIKELY(fromArr)) {
3655
0
      const SmallHermesValue elem = fromArr->at(runtime, fromIndex);
3656
0
      if (!elem.isEmpty()) {
3657
0
        fromValueHandle = elem.unboxToHV(runtime);
3658
0
      }
3659
0
    }
3660
    // Slow path
3661
0
    else {
3662
0
      fromIndexHandle = HermesValue::encodeTrustedNumberValue(fromIndex);
3663
3664
0
      CallResult<PseudoHandle<>> propRes =
3665
0
          JSObject::getComputed_RJS(from, runtime, fromIndexHandle);
3666
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3667
0
        return ExecutionStatus::EXCEPTION;
3668
0
      }
3669
0
      fromValueHandle = propRes->getHermesValue();
3670
0
    }
3671
3672
0
    toIndexHandle = HermesValue::encodeTrustedNumberValue(toStartIndex + i);
3673
3674
0
    if (LLVM_UNLIKELY(
3675
0
            JSObject::defineOwnComputedPrimitive(
3676
0
                to,
3677
0
                runtime,
3678
0
                toIndexHandle,
3679
0
                DefinePropertyFlags::getDefaultNewPropertyFlags(),
3680
0
                fromValueHandle,
3681
0
                PropOpFlags().plusThrowOnError()) ==
3682
0
            ExecutionStatus::EXCEPTION)) {
3683
0
      return ExecutionStatus::EXCEPTION;
3684
0
    }
3685
3686
0
    ++i;
3687
0
  }
3688
3689
0
  return i;
3690
0
}
3691
3692
/// ES14.0 23.1.3.35
3693
CallResult<HermesValue>
3694
0
arrayPrototypeToSpliced(void *, Runtime &runtime, NativeArgs args) {
3695
0
  GCScope gcScope{runtime};
3696
3697
  // 1. Let O be ? ToObject(this value).
3698
0
  auto oRes = toObject(runtime, args.getThisHandle());
3699
0
  if (LLVM_UNLIKELY(oRes == ExecutionStatus::EXCEPTION)) {
3700
0
    return ExecutionStatus::EXCEPTION;
3701
0
  }
3702
0
  auto O = runtime.makeHandle<JSObject>(*oRes);
3703
3704
  // 2. Let len be ? LengthOfArrayLike(O).
3705
0
  Handle<JSArray> jsArr = Handle<JSArray>::dyn_vmcast(O);
3706
0
  auto lenRes = lengthOfArrayLike(runtime, O, jsArr);
3707
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
3708
0
    return ExecutionStatus::EXCEPTION;
3709
0
  }
3710
0
  double len = lenRes.getValue();
3711
3712
  // 3. Let relativeIndex be ? ToIntegerOrInfinity(index).
3713
0
  auto relativeStartRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
3714
0
  if (LLVM_UNLIKELY(relativeStartRes == ExecutionStatus::EXCEPTION)) {
3715
0
    return ExecutionStatus::EXCEPTION;
3716
0
  }
3717
  // Use double here, because ToInteger may return Infinity.
3718
0
  double relativeStart = relativeStartRes->getNumber();
3719
3720
  // 4. If relativeStart is -∞, let actualStart be 0.
3721
  // 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart,
3722
  // 0).
3723
  // 6. Else, let actualStart be min(relativeStart, len).
3724
0
  double actualStart = relativeStart < 0 ? std::max(len + relativeStart, 0.0)
3725
0
                                         : std::min(relativeStart, len);
3726
3727
0
  uint32_t argCount = args.getArgCount();
3728
0
  uint64_t actualSkipCount;
3729
0
  uint64_t insertCount;
3730
3731
0
  switch (argCount) {
3732
    // 8. If start is not present, then
3733
0
    case 0:
3734
0
      insertCount = 0;
3735
      // 8a. Let actualSkipCount be 0.
3736
0
      actualSkipCount = 0;
3737
0
      break;
3738
    // 9. Else if skipCount is not present, then
3739
0
    case 1:
3740
0
      insertCount = 0;
3741
      // 9a. Let actualSkipCount be len - actualStart.
3742
0
      actualSkipCount = len - actualStart;
3743
0
      break;
3744
    // 10. Else
3745
0
    default: {
3746
      // 10a. Let sc be ? ToIntegerOrInfinity(skipCount).
3747
0
      auto skipCountRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
3748
0
      if (LLVM_UNLIKELY(skipCountRes == ExecutionStatus::EXCEPTION)) {
3749
0
        return ExecutionStatus::EXCEPTION;
3750
0
      }
3751
3752
0
      insertCount = argCount - 2;
3753
3754
      // 10b. Let actualSkipCount be the result of clamping dc between 0 and len
3755
      // - actualStart.
3756
0
      actualSkipCount =
3757
0
          std::min(std::max(skipCountRes->getNumber(), 0.0), len - actualStart);
3758
0
    }
3759
0
  }
3760
3761
  // 11. Let newLen be len + insertCount - actualSkipCount.
3762
0
  auto lenAfterInsert = len + insertCount - actualSkipCount;
3763
3764
  // 12. If newLen > 253 - 1, throw a TypeError exception.
3765
0
  if (LLVM_UNLIKELY(
3766
          // lenAfterInsert < len ||
3767
0
          lenAfterInsert - actualSkipCount > (1LLU << 53) - 1)) {
3768
0
    return runtime.raiseTypeError(
3769
0
        "Array.prototype.toSpliced result out of space");
3770
0
  }
3771
3772
  // 13. Let A be ArrayCreate(len).
3773
0
  uint32_t lenAfterInsert32 = truncateToUInt32(lenAfterInsert);
3774
0
  if (LLVM_UNLIKELY(lenAfterInsert32 != lenAfterInsert)) {
3775
0
    return runtime.raiseRangeError("invalid array length");
3776
0
  }
3777
0
  auto ARes = JSArray::create(runtime, lenAfterInsert32, lenAfterInsert32);
3778
0
  if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) {
3779
0
    return ExecutionStatus::EXCEPTION;
3780
0
  }
3781
0
  auto A = ARes.getValue();
3782
3783
  // 14. Let i be 0
3784
0
  double i = 0;
3785
0
  MutableHandle<> insertIndexHandle{runtime};
3786
3787
  // 15. Let r be actualStart + actualSkipCount.
3788
0
  uint64_t r = actualStart + actualSkipCount;
3789
3790
0
  uint64_t paramIndex = 2;
3791
3792
0
  Handle<JSArray> fromArr = Handle<JSArray>::dyn_vmcast(O);
3793
3794
  // 16a - 16d
3795
  // Copy elements from original array O from beginning until actualStart into
3796
  // new array A
3797
0
  auto copyRes =
3798
0
      arrayCopyHelper(runtime, gcScope, O, fromArr, 0, A, 0, actualStart);
3799
0
  if (LLVM_UNLIKELY(copyRes == ExecutionStatus::EXCEPTION)) {
3800
0
    return ExecutionStatus::EXCEPTION;
3801
0
  }
3802
0
  i += copyRes.getValue();
3803
3804
  // 17. For each element E of items, do
3805
0
  while (paramIndex < argCount) {
3806
    // 17a. Let Pi be ! ToString(𝔽(i)).
3807
0
    insertIndexHandle = HermesValue::encodeTrustedNumberValue(i);
3808
3809
    // 17b. Perform ! CreateDataPropertyOrThrow(A, Pi, E).
3810
0
    if (LLVM_UNLIKELY(
3811
0
            JSObject::defineOwnComputedPrimitive(
3812
0
                A,
3813
0
                runtime,
3814
0
                insertIndexHandle,
3815
0
                DefinePropertyFlags::getDefaultNewPropertyFlags(),
3816
0
                args.getArgHandle(paramIndex),
3817
0
                PropOpFlags().plusThrowOnError()) ==
3818
0
            ExecutionStatus::EXCEPTION)) {
3819
0
      return ExecutionStatus::EXCEPTION;
3820
0
    }
3821
3822
    // 17c. Set i to i + 1.
3823
0
    ++i;
3824
0
    ++paramIndex;
3825
0
  }
3826
3827
  // 18a - 18f
3828
  // Copy remaining elements from original array O including skipCount into new
3829
  // array A
3830
0
  copyRes = arrayCopyHelper(
3831
0
      runtime, gcScope, O, fromArr, r, A, i, lenAfterInsert - i);
3832
0
  if (LLVM_UNLIKELY(copyRes == ExecutionStatus::EXCEPTION)) {
3833
0
    return ExecutionStatus::EXCEPTION;
3834
0
  }
3835
0
  i += copyRes.getValue();
3836
3837
0
  return A.getHermesValue();
3838
0
}
3839
3840
/// ES14.0 23.1.3.39
3841
CallResult<HermesValue>
3842
0
arrayPrototypeWith(void *, Runtime &runtime, NativeArgs args) {
3843
0
  GCScope gcScope{runtime};
3844
3845
  // 1. Let O be ? ToObject(this value).
3846
0
  auto oRes = toObject(runtime, args.getThisHandle());
3847
0
  if (LLVM_UNLIKELY(oRes == ExecutionStatus::EXCEPTION)) {
3848
0
    return ExecutionStatus::EXCEPTION;
3849
0
  }
3850
0
  auto O = runtime.makeHandle<JSObject>(*oRes);
3851
3852
  // 2. Let len be ? LengthOfArrayLike(O).
3853
0
  Handle<JSArray> jsArr = Handle<JSArray>::dyn_vmcast(O);
3854
0
  auto lenRes = lengthOfArrayLike(runtime, O, jsArr);
3855
0
  if (LLVM_UNLIKELY(lenRes == ExecutionStatus::EXCEPTION)) {
3856
0
    return ExecutionStatus::EXCEPTION;
3857
0
  }
3858
0
  auto len = lenRes.getValue();
3859
3860
  // 3. Let relativeIndex be ? ToIntegerOrInfinity(index).
3861
0
  auto relativeIndexRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
3862
0
  if (LLVM_UNLIKELY(relativeIndexRes == ExecutionStatus::EXCEPTION)) {
3863
0
    return ExecutionStatus::EXCEPTION;
3864
0
  }
3865
  // Use double here, because ToInteger may return Infinity.
3866
0
  double relativeIndex = relativeIndexRes->getNumber();
3867
3868
0
  double actualIndex = 0;
3869
  // 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
3870
0
  if (relativeIndex >= 0) {
3871
0
    actualIndex = relativeIndex;
3872
0
  }
3873
  // 5. Else, let actualIndex be len + relativeIndex.
3874
0
  else {
3875
0
    actualIndex = len + relativeIndex;
3876
0
  }
3877
3878
  // 6. If actualIndex ≥ len or actualIndex < 0, throw a RangeError exception.
3879
0
  if (actualIndex < 0 || actualIndex >= len) {
3880
0
    return runtime.raiseRangeError("invalid or out-of-range index");
3881
0
  }
3882
3883
  // 7. Let A be ArrayCreate(len).
3884
0
  uint32_t len32 = truncateToUInt32(len);
3885
0
  if (LLVM_UNLIKELY(len32 != len)) {
3886
0
    return runtime.raiseRangeError("invalid array length");
3887
0
  }
3888
0
  auto ARes = JSArray::create(runtime, len32, len32);
3889
0
  if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) {
3890
0
    return ExecutionStatus::EXCEPTION;
3891
0
  }
3892
0
  auto A = ARes.getValue();
3893
3894
  // 8. Let k be 0.
3895
0
  double k = 0;
3896
3897
0
  MutableHandle<> kHandle{runtime};
3898
0
  MutableHandle<> fromValueHandle{runtime};
3899
3900
0
  auto marker = gcScope.createMarker();
3901
  // 9. Repeat, while k < len,
3902
0
  while (k < len) {
3903
0
    gcScope.flushToMarker(marker);
3904
3905
    // 9a. Let Pk be the result of ? Get(O, ! ToString(k)).
3906
0
    kHandle = HermesValue::encodeUntrustedNumberValue(k);
3907
0
    auto PkRes = JSObject::getComputed_RJS(O, runtime, kHandle);
3908
0
    if (LLVM_UNLIKELY(PkRes == ExecutionStatus::EXCEPTION)) {
3909
0
      return ExecutionStatus::EXCEPTION;
3910
0
    }
3911
3912
    // 9b. If k is actualIndex, let fromValue be value.
3913
0
    if (k == actualIndex) {
3914
0
      fromValueHandle = args.getArgHandle(1);
3915
0
    }
3916
    // 9c. Else, let fromValue be ? Get(O, Pk).
3917
0
    else {
3918
0
      if (LLVM_LIKELY(jsArr)) {
3919
0
        const SmallHermesValue elm = jsArr->at(runtime, k);
3920
        // If the element is not empty, we can return it directly here.
3921
        // Otherwise, we must proceed to the slow path.
3922
0
        if (!elm.isEmpty()) {
3923
0
          fromValueHandle = elm.unboxToHV(runtime);
3924
0
        }
3925
0
      }
3926
      // Slow path
3927
0
      else {
3928
0
        CallResult<PseudoHandle<>> propRes =
3929
0
            JSObject::getComputed_RJS(O, runtime, kHandle);
3930
0
        if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
3931
0
          return ExecutionStatus::EXCEPTION;
3932
0
        }
3933
0
        fromValueHandle = propRes->getHermesValue();
3934
0
      }
3935
0
    }
3936
3937
    // 9d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
3938
0
    if (LLVM_UNLIKELY(
3939
0
            JSObject::defineOwnComputedPrimitive(
3940
0
                A,
3941
0
                runtime,
3942
0
                kHandle,
3943
0
                DefinePropertyFlags::getDefaultNewPropertyFlags(),
3944
0
                fromValueHandle,
3945
0
                PropOpFlags().plusThrowOnError()) ==
3946
0
            ExecutionStatus::EXCEPTION)) {
3947
0
      return ExecutionStatus::EXCEPTION;
3948
0
    }
3949
3950
    // 9e. Set k to k + 1.
3951
0
    ++k;
3952
0
  }
3953
3954
0
  return A.getHermesValue();
3955
0
}
3956
3957
0
CallResult<HermesValue> arrayOf(void *, Runtime &runtime, NativeArgs args) {
3958
0
  GCScope gcScope{runtime};
3959
3960
  // 1. Let len be the actual number of arguments passed to this function.
3961
0
  uint32_t len = args.getArgCount();
3962
  // 2. Let items be the List of arguments passed to this function.
3963
  // 3. Let C be the this value.
3964
0
  auto C = args.getThisHandle();
3965
3966
0
  MutableHandle<JSObject> A{runtime};
3967
0
  CallResult<bool> isConstructorRes = isConstructor(runtime, *C);
3968
0
  if (LLVM_UNLIKELY(isConstructorRes == ExecutionStatus::EXCEPTION)) {
3969
0
    return ExecutionStatus::EXCEPTION;
3970
0
  }
3971
  // 4. If IsConstructor(C) is true, then
3972
0
  if (*isConstructorRes) {
3973
    // a. Let A be Construct(C, «len»).
3974
0
    auto aRes = Callable::executeConstruct1(
3975
0
        Handle<Callable>::vmcast(C),
3976
0
        runtime,
3977
0
        runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(len)));
3978
0
    if (LLVM_UNLIKELY(aRes == ExecutionStatus::EXCEPTION)) {
3979
0
      return ExecutionStatus::EXCEPTION;
3980
0
    }
3981
0
    A = PseudoHandle<JSObject>::vmcast(std::move(*aRes));
3982
0
  } else {
3983
    // 5. Else,
3984
    // a. Let A be ArrayCreate(len).
3985
0
    auto aRes = JSArray::create(runtime, len, len);
3986
0
    if (LLVM_UNLIKELY(aRes == ExecutionStatus::EXCEPTION)) {
3987
0
      return ExecutionStatus::EXCEPTION;
3988
0
    }
3989
0
    A = vmcast<JSObject>(aRes->getHermesValue());
3990
0
  }
3991
  // 7. Let k be 0.
3992
0
  MutableHandle<> k{runtime, HermesValue::encodeUntrustedNumberValue(0)};
3993
0
  MutableHandle<> kValue{runtime};
3994
3995
0
  GCScopeMarkerRAII marker{gcScope};
3996
  // 8. Repeat, while k < len
3997
0
  for (; k->getNumberAs<uint32_t>() < len; marker.flush()) {
3998
    // a. Let kValue be items[k].
3999
0
    kValue = args.getArg(k->getNumber());
4000
4001
    // c. Let defineStatus be CreateDataPropertyOrThrow(A,Pk, kValue).
4002
0
    if (LLVM_UNLIKELY(
4003
0
            JSObject::defineOwnComputedPrimitive(
4004
0
                A,
4005
0
                runtime,
4006
0
                k,
4007
0
                DefinePropertyFlags::getDefaultNewPropertyFlags(),
4008
0
                kValue,
4009
0
                PropOpFlags().plusThrowOnError()) ==
4010
0
            ExecutionStatus::EXCEPTION)) {
4011
0
      return ExecutionStatus::EXCEPTION;
4012
0
    }
4013
4014
    // e. Increase k by 1.
4015
0
    k = HermesValue::encodeUntrustedNumberValue(k->getNumber() + 1);
4016
0
  }
4017
4018
  // 9. Let setStatus be Set(A, "length", len, true).
4019
  // 10. ReturnIfAbrupt(setStatus).
4020
0
  auto setStatus = JSObject::putNamed_RJS(
4021
0
      A,
4022
0
      runtime,
4023
0
      Predefined::getSymbolID(Predefined::length),
4024
0
      runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(len)),
4025
0
      PropOpFlags().plusThrowOnError());
4026
0
  if (LLVM_UNLIKELY(setStatus == ExecutionStatus::EXCEPTION)) {
4027
0
    return ExecutionStatus::EXCEPTION;
4028
0
  }
4029
4030
  // 11. Return A.
4031
0
  return A.getHermesValue();
4032
0
}
4033
4034
/// ES6.0 22.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] )
4035
0
CallResult<HermesValue> arrayFrom(void *, Runtime &runtime, NativeArgs args) {
4036
0
  GCScope gcScope{runtime};
4037
0
  auto itemsHandle = args.getArgHandle(0);
4038
  // 1. Let C be the this value.
4039
0
  auto C = args.getThisHandle();
4040
  // 2. If mapfn is undefined, let mapping be false.
4041
  // 3. else
4042
0
  MutableHandle<Callable> mapfn{runtime};
4043
0
  MutableHandle<> T{runtime, HermesValue::encodeUndefinedValue()};
4044
0
  if (!args.getArg(1).isUndefined()) {
4045
0
    mapfn = dyn_vmcast<Callable>(args.getArg(1));
4046
    // a. If IsCallable(mapfn) is false, throw a TypeError exception.
4047
0
    if (LLVM_UNLIKELY(!mapfn)) {
4048
0
      return runtime.raiseTypeError("Mapping function is not callable.");
4049
0
    }
4050
    // b. If thisArg was supplied, let T be thisArg; else let T be undefined.
4051
0
    if (args.getArgCount() >= 3) {
4052
0
      T = args.getArg(2);
4053
0
    }
4054
    // c. Let mapping be true
4055
0
  }
4056
  // 4. Let usingIterator be GetMethod(items, @@iterator).
4057
  // 5. ReturnIfAbrupt(usingIterator).
4058
0
  auto methodRes = getMethod(
4059
0
      runtime,
4060
0
      itemsHandle,
4061
0
      runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolIterator)));
4062
0
  if (LLVM_UNLIKELY(methodRes == ExecutionStatus::EXCEPTION)) {
4063
0
    return ExecutionStatus::EXCEPTION;
4064
0
  }
4065
0
  auto usingIterator = runtime.makeHandle(methodRes->getHermesValue());
4066
4067
0
  MutableHandle<JSObject> A{runtime};
4068
  // 6. If usingIterator is not undefined, then
4069
0
  if (!usingIterator->isUndefined()) {
4070
0
    CallResult<bool> isConstructorRes = isConstructor(runtime, *C);
4071
0
    if (LLVM_UNLIKELY(isConstructorRes == ExecutionStatus::EXCEPTION)) {
4072
0
      return ExecutionStatus::EXCEPTION;
4073
0
    }
4074
    // a. If IsConstructor(C) is true, then
4075
0
    if (*isConstructorRes) {
4076
0
      GCScopeMarkerRAII markerConstruct{gcScope};
4077
      // i. Let A be Construct(C).
4078
0
      auto callRes =
4079
0
          Callable::executeConstruct0(Handle<Callable>::vmcast(C), runtime);
4080
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
4081
0
        return ExecutionStatus::EXCEPTION;
4082
0
      }
4083
0
      A = PseudoHandle<JSObject>::vmcast(std::move(*callRes));
4084
0
    } else {
4085
      // b. Else,
4086
      //  i. Let A be ArrayCreate(0).
4087
0
      auto arrRes = JSArray::create(runtime, 0, 0);
4088
0
      if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
4089
0
        return ExecutionStatus::EXCEPTION;
4090
0
      }
4091
0
      A = arrRes->get();
4092
0
    }
4093
    // c. ReturnIfAbrupt(A).
4094
    // d. Let iterator be GetIterator(items, usingIterator).
4095
    // Assert we can cast usingIterator to a Callable otherwise getMethod would
4096
    // have thrown.
4097
    // e. ReturnIfAbrupt(iterator).
4098
0
    auto iterRes = getIterator(
4099
0
        runtime, args.getArgHandle(0), Handle<Callable>::vmcast(usingIterator));
4100
0
    if (LLVM_UNLIKELY(iterRes == ExecutionStatus::EXCEPTION)) {
4101
0
      return ExecutionStatus::EXCEPTION;
4102
0
    }
4103
0
    auto iteratorRecord = *iterRes;
4104
    // f. Let k be 0.
4105
0
    MutableHandle<> k{runtime, HermesValue::encodeUntrustedNumberValue(0)};
4106
    // g. Repeat
4107
0
    MutableHandle<> mappedValue{runtime};
4108
0
    MutableHandle<> nextValue{runtime};
4109
0
    while (true) {
4110
0
      GCScopeMarkerRAII marker1{runtime};
4111
      // ii. Let next be IteratorStep(iteratorRecord).
4112
      // iii. ReturnIfAbrupt(next).
4113
0
      auto next = iteratorStep(runtime, iteratorRecord);
4114
0
      if (LLVM_UNLIKELY(next == ExecutionStatus::EXCEPTION)) {
4115
0
        return ExecutionStatus::EXCEPTION;
4116
0
      }
4117
      // iv. If next is false, then
4118
0
      if (!next.getValue()) {
4119
        // 1. Let setStatus be Set(A, "length", k, true).
4120
        // 2. ReturnIfAbrupt(setStatus).
4121
        // 3. Return A.
4122
0
        auto setStatus = JSObject::putNamed_RJS(
4123
0
            A,
4124
0
            runtime,
4125
0
            Predefined::getSymbolID(Predefined::length),
4126
0
            k,
4127
0
            PropOpFlags().plusThrowOnError());
4128
0
        if (LLVM_UNLIKELY(setStatus == ExecutionStatus::EXCEPTION)) {
4129
0
          return ExecutionStatus::EXCEPTION;
4130
0
        }
4131
0
        return A.getHermesValue();
4132
0
      }
4133
      // v. Let nextValue be IteratorValue(next).
4134
      // vi. ReturnIfAbrupt(nextValue).
4135
0
      auto propRes = JSObject::getNamed_RJS(
4136
0
          *next, runtime, Predefined::getSymbolID(Predefined::value));
4137
0
      if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
4138
0
        return ExecutionStatus::EXCEPTION;
4139
0
      }
4140
0
      nextValue = std::move(*propRes);
4141
      // vii. If mapping is true, then
4142
0
      if (mapfn) {
4143
        // 1. Let mappedValue be Call(mapfn, T, «nextValue, k»).
4144
0
        auto callRes = Callable::executeCall2(
4145
0
            mapfn, runtime, T, nextValue.getHermesValue(), k.getHermesValue());
4146
        // 2. If mappedValue is an abrupt completion, return
4147
        // IteratorClose(iterator, mappedValue).
4148
0
        if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
4149
0
          return iteratorCloseAndRethrow(runtime, iteratorRecord.iterator);
4150
0
        }
4151
        // 3. Let mappedValue be mappedValue.[[value]].
4152
0
        mappedValue = std::move(*callRes);
4153
0
      } else {
4154
        // viii. Else, let mappedValue be nextValue.
4155
0
        mappedValue = nextValue.getHermesValue();
4156
0
      }
4157
      // ix. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue).
4158
      // x. If defineStatus is an abrupt completion, return
4159
      // IteratorClose(iterator, defineStatus).
4160
0
      if (LLVM_UNLIKELY(
4161
0
              JSObject::defineOwnComputedPrimitive(
4162
0
                  A,
4163
0
                  runtime,
4164
0
                  k,
4165
0
                  DefinePropertyFlags::getDefaultNewPropertyFlags(),
4166
0
                  mappedValue,
4167
0
                  PropOpFlags().plusThrowOnError()) ==
4168
0
              ExecutionStatus::EXCEPTION)) {
4169
0
        return iteratorCloseAndRethrow(runtime, iteratorRecord.iterator);
4170
0
      }
4171
      // xi. Increase k by 1.
4172
0
      k = HermesValue::encodeUntrustedNumberValue(k->getNumber() + 1);
4173
0
    }
4174
0
  }
4175
  // 7. Assert: items is not an Iterable so assume it is an array-like object.
4176
  // 8. Let arrayLike be ToObject(items).
4177
0
  auto objRes = toObject(runtime, itemsHandle);
4178
  // 9. ReturnIfAbrupt(arrayLike).
4179
0
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
4180
0
    return ExecutionStatus::EXCEPTION;
4181
0
  }
4182
0
  auto arrayLike = runtime.makeHandle<JSObject>(objRes.getValue());
4183
  // 10. Let len be ToLength(Get(arrayLike, "length")).
4184
  // 11. ReturnIfAbrupt(len).
4185
0
  auto propRes = JSObject::getNamed_RJS(
4186
0
      arrayLike, runtime, Predefined::getSymbolID(Predefined::length));
4187
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
4188
0
    return ExecutionStatus::EXCEPTION;
4189
0
  }
4190
0
  auto lengthRes = toLength(runtime, runtime.makeHandle(std::move(*propRes)));
4191
0
  if (LLVM_UNLIKELY(lengthRes == ExecutionStatus::EXCEPTION)) {
4192
0
    return ExecutionStatus::EXCEPTION;
4193
0
  }
4194
0
  uint64_t len = lengthRes->getNumberAs<uint64_t>();
4195
0
  CallResult<bool> isConstructorRes = isConstructor(runtime, *C);
4196
0
  if (LLVM_UNLIKELY(isConstructorRes == ExecutionStatus::EXCEPTION)) {
4197
0
    return ExecutionStatus::EXCEPTION;
4198
0
  }
4199
  // 12. If IsConstructor(C) is true, then
4200
0
  if (*isConstructorRes) {
4201
    // a. Let A be Construct(C, «len»).
4202
0
    auto callRes = Callable::executeConstruct1(
4203
0
        Handle<Callable>::vmcast(C),
4204
0
        runtime,
4205
0
        runtime.makeHandle(lengthRes.getValue()));
4206
0
    if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
4207
0
      return ExecutionStatus::EXCEPTION;
4208
0
    }
4209
0
    A = PseudoHandle<JSObject>::vmcast(std::move(*callRes));
4210
0
  } else {
4211
    // 13. Else,
4212
    //  a. Let A be ArrayCreate(len).
4213
0
    if (LLVM_UNLIKELY(len > JSArray::StorageType::maxElements())) {
4214
0
      return runtime.raiseRangeError("Out of memory for array elements.");
4215
0
    }
4216
0
    auto arrRes = JSArray::create(runtime, len, len);
4217
0
    if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
4218
0
      return ExecutionStatus::EXCEPTION;
4219
0
    }
4220
0
    A = arrRes->get();
4221
0
  }
4222
  // 14. ReturnIfAbrupt(A).
4223
  // 15. Let k be 0.
4224
0
  MutableHandle<> k{runtime, HermesValue::encodeUntrustedNumberValue(0)};
4225
  // 16. Repeat, while k < len
4226
0
  MutableHandle<> mappedValue{runtime};
4227
0
  while (k->getNumberAs<uint32_t>() < len) {
4228
0
    GCScopeMarkerRAII marker2{runtime};
4229
    // b. Let kValue be Get(arrayLike, Pk).
4230
0
    propRes = JSObject::getComputed_RJS(arrayLike, runtime, k);
4231
    // c. ReturnIfAbrupt(kValue).
4232
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
4233
0
      return ExecutionStatus::EXCEPTION;
4234
0
    }
4235
    // d. If mapping is true, then
4236
0
    if (mapfn) {
4237
      // i. Let mappedValue be Call(mapfn, T, «kValue, k»).
4238
      // ii. ReturnIfAbrupt(mappedValue).
4239
0
      auto callRes = Callable::executeCall2(
4240
0
          mapfn, runtime, T, propRes->get(), k.getHermesValue());
4241
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
4242
0
        return ExecutionStatus::EXCEPTION;
4243
0
      }
4244
0
      mappedValue = std::move(*callRes);
4245
0
    } else {
4246
      // e. Else, let mappedValue be kValue.
4247
0
      mappedValue = std::move(*propRes);
4248
0
    }
4249
    // f. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue).
4250
    // g. ReturnIfAbrupt(defineStatus).
4251
0
    if (LLVM_UNLIKELY(
4252
0
            JSObject::defineOwnComputedPrimitive(
4253
0
                A,
4254
0
                runtime,
4255
0
                k,
4256
0
                DefinePropertyFlags::getDefaultNewPropertyFlags(),
4257
0
                mappedValue,
4258
0
                PropOpFlags().plusThrowOnError()) ==
4259
0
            ExecutionStatus::EXCEPTION)) {
4260
0
      return ExecutionStatus::EXCEPTION;
4261
0
    }
4262
    // h. Increase k by 1.
4263
0
    k = HermesValue::encodeUntrustedNumberValue(k->getNumber() + 1);
4264
0
  }
4265
  // 17. Let setStatus be Set(A, "length", len, true).
4266
0
  auto setStatus = JSObject::putNamed_RJS(
4267
0
      A,
4268
0
      runtime,
4269
0
      Predefined::getSymbolID(Predefined::length),
4270
0
      k,
4271
0
      PropOpFlags().plusThrowOnError());
4272
  // 18. ReturnIfAbrupt(setStatus).
4273
0
  if (LLVM_UNLIKELY(setStatus == ExecutionStatus::EXCEPTION)) {
4274
0
    return ExecutionStatus::EXCEPTION;
4275
0
  }
4276
  // 19. Return A.
4277
0
  return A.getHermesValue();
4278
0
}
4279
4280
} // namespace vm
4281
} // namespace hermes