Coverage Report

Created: 2025-12-11 06:40

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