Coverage Report

Created: 2023-11-19 07:23

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