Coverage Report

Created: 2025-12-11 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/lib/VM/JSLib/String.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
//===----------------------------------------------------------------------===//
9
/// \file
10
/// ES5.1 15.5 Initialize the String constructor.
11
//===----------------------------------------------------------------------===//
12
13
#include "JSLibInternal.h"
14
15
#include "hermes/Platform/Unicode/PlatformUnicode.h"
16
#include "hermes/VM/Operations.h"
17
#include "hermes/VM/PrimitiveBox.h"
18
#include "hermes/VM/SmallXString.h"
19
#include "hermes/VM/StringBuilder.h"
20
#include "hermes/VM/StringView.h"
21
22
#if defined(__ANDROID__)
23
#include "hermes/Platform/Unicode/PlatformUnicode.h"
24
#endif
25
26
#ifdef __APPLE__
27
#include <CoreFoundation/CFString.h>
28
#endif
29
30
#include <locale>
31
#pragma GCC diagnostic push
32
33
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
34
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
35
#endif
36
namespace hermes {
37
namespace vm {
38
39
//===----------------------------------------------------------------------===//
40
/// String.
41
42
94
Handle<JSObject> createStringConstructor(Runtime &runtime) {
43
94
  auto stringPrototype = Handle<JSString>::vmcast(&runtime.stringPrototype);
44
45
94
  auto cons = defineSystemConstructor<JSString>(
46
94
      runtime,
47
94
      Predefined::getSymbolID(Predefined::String),
48
94
      stringConstructor,
49
94
      stringPrototype,
50
94
      1,
51
94
      CellKind::JSStringKind);
52
53
  // String.prototype.xxx methods.
54
94
  void *ctx = nullptr;
55
94
  defineMethod(
56
94
      runtime,
57
94
      stringPrototype,
58
94
      Predefined::getSymbolID(Predefined::toString),
59
94
      ctx,
60
94
      stringPrototypeToString,
61
94
      0);
62
94
  defineMethod(
63
94
      runtime,
64
94
      stringPrototype,
65
94
      Predefined::getSymbolID(Predefined::at),
66
94
      ctx,
67
94
      stringPrototypeAt,
68
94
      1);
69
94
  defineMethod(
70
94
      runtime,
71
94
      stringPrototype,
72
94
      Predefined::getSymbolID(Predefined::valueOf),
73
94
      ctx,
74
94
      stringPrototypeToString,
75
94
      0);
76
94
  defineMethod(
77
94
      runtime,
78
94
      stringPrototype,
79
94
      Predefined::getSymbolID(Predefined::charCodeAt),
80
94
      ctx,
81
94
      stringPrototypeCharCodeAt,
82
94
      1);
83
94
  defineMethod(
84
94
      runtime,
85
94
      stringPrototype,
86
94
      Predefined::getSymbolID(Predefined::codePointAt),
87
94
      ctx,
88
94
      stringPrototypeCodePointAt,
89
94
      1);
90
94
  defineMethod(
91
94
      runtime,
92
94
      stringPrototype,
93
94
      Predefined::getSymbolID(Predefined::concat),
94
94
      ctx,
95
94
      stringPrototypeConcat,
96
94
      1);
97
94
  defineMethod(
98
94
      runtime,
99
94
      stringPrototype,
100
94
      Predefined::getSymbolID(Predefined::substring),
101
94
      ctx,
102
94
      stringPrototypeSubstring,
103
94
      2);
104
94
  defineMethod(
105
94
      runtime,
106
94
      stringPrototype,
107
94
      Predefined::getSymbolID(Predefined::toLowerCase),
108
94
      ctx,
109
94
      stringPrototypeToLowerCase,
110
94
      0);
111
94
  defineMethod(
112
94
      runtime,
113
94
      stringPrototype,
114
94
      Predefined::getSymbolID(Predefined::toLocaleLowerCase),
115
94
      ctx,
116
94
      stringPrototypeToLocaleLowerCase,
117
94
      0);
118
94
  defineMethod(
119
94
      runtime,
120
94
      stringPrototype,
121
94
      Predefined::getSymbolID(Predefined::toUpperCase),
122
94
      ctx,
123
94
      stringPrototypeToUpperCase,
124
94
      0);
125
94
  defineMethod(
126
94
      runtime,
127
94
      stringPrototype,
128
94
      Predefined::getSymbolID(Predefined::toLocaleUpperCase),
129
94
      ctx,
130
94
      stringPrototypeToLocaleUpperCase,
131
94
      0);
132
94
  defineMethod(
133
94
      runtime,
134
94
      stringPrototype,
135
94
      Predefined::getSymbolID(Predefined::substr),
136
94
      ctx,
137
94
      stringPrototypeSubstr,
138
94
      2);
139
94
  defineMethod(
140
94
      runtime,
141
94
      stringPrototype,
142
94
      Predefined::getSymbolID(Predefined::trim),
143
94
      ctx,
144
94
      stringPrototypeTrim,
145
94
      0);
146
94
  defineMethod(
147
94
      runtime,
148
94
      stringPrototype,
149
94
      Predefined::getSymbolID(Predefined::localeCompare),
150
94
      ctx,
151
94
      stringPrototypeLocaleCompare,
152
94
      1);
153
94
  defineMethod(
154
94
      runtime,
155
94
      stringPrototype,
156
94
      Predefined::getSymbolID(Predefined::normalize),
157
94
      ctx,
158
94
      stringPrototypeNormalize,
159
94
      0);
160
94
  defineMethod(
161
94
      runtime,
162
94
      stringPrototype,
163
94
      Predefined::getSymbolID(Predefined::repeat),
164
94
      ctx,
165
94
      stringPrototypeRepeat,
166
94
      1);
167
168
94
  DefinePropertyFlags dpf = DefinePropertyFlags::getNewNonEnumerableFlags();
169
94
  auto trimStartRes =
170
94
      runtime.makeHandle<Callable>(runtime.ignoreAllocationFailure(defineMethod(
171
94
          runtime,
172
94
          stringPrototype,
173
94
          Predefined::getSymbolID(Predefined::trimStart),
174
94
          ctx,
175
94
          stringPrototypeTrimStart,
176
94
          0,
177
94
          dpf)));
178
94
  auto trimEndRes =
179
94
      runtime.makeHandle<Callable>(runtime.ignoreAllocationFailure(defineMethod(
180
94
          runtime,
181
94
          stringPrototype,
182
94
          Predefined::getSymbolID(Predefined::trimEnd),
183
94
          ctx,
184
94
          stringPrototypeTrimEnd,
185
94
          0,
186
94
          dpf)));
187
188
94
  defineProperty(
189
94
      runtime,
190
94
      stringPrototype,
191
94
      Predefined::getSymbolID(Predefined::trimLeft),
192
94
      trimStartRes);
193
94
  defineProperty(
194
94
      runtime,
195
94
      stringPrototype,
196
94
      Predefined::getSymbolID(Predefined::trimRight),
197
94
      trimEndRes);
198
199
94
  (void)defineMethod(
200
94
      runtime,
201
94
      stringPrototype,
202
94
      Predefined::getSymbolID(Predefined::SymbolIterator),
203
94
      Predefined::getSymbolID(Predefined::squareSymbolIterator),
204
94
      ctx,
205
94
      stringPrototypeSymbolIterator,
206
94
      0,
207
94
      dpf);
208
209
  // String.xxx() methods.
210
94
  defineMethod(
211
94
      runtime,
212
94
      cons,
213
94
      Predefined::getSymbolID(Predefined::fromCharCode),
214
94
      ctx,
215
94
      stringFromCharCode,
216
94
      1);
217
94
  defineMethod(
218
94
      runtime,
219
94
      cons,
220
94
      Predefined::getSymbolID(Predefined::fromCodePoint),
221
94
      ctx,
222
94
      stringFromCodePoint,
223
94
      1);
224
94
  defineMethod(
225
94
      runtime,
226
94
      cons,
227
94
      Predefined::getSymbolID(Predefined::raw),
228
94
      ctx,
229
94
      stringRaw,
230
94
      1);
231
94
  defineMethod(
232
94
      runtime,
233
94
      stringPrototype,
234
94
      Predefined::getSymbolID(Predefined::matchAll),
235
94
      ctx,
236
94
      stringPrototypeMatchAll,
237
94
      1);
238
94
  defineMethod(
239
94
      runtime,
240
94
      stringPrototype,
241
94
      Predefined::getSymbolID(Predefined::replaceAll),
242
94
      ctx,
243
94
      stringPrototypeReplaceAll,
244
94
      2);
245
94
  defineMethod(
246
94
      runtime,
247
94
      stringPrototype,
248
94
      Predefined::getSymbolID(Predefined::match),
249
94
      ctx,
250
94
      stringPrototypeMatch,
251
94
      1);
252
94
  defineMethod(
253
94
      runtime,
254
94
      stringPrototype,
255
94
      Predefined::getSymbolID(Predefined::padEnd),
256
94
      (void *)false,
257
94
      stringPrototypePad,
258
94
      1);
259
94
  defineMethod(
260
94
      runtime,
261
94
      stringPrototype,
262
94
      Predefined::getSymbolID(Predefined::padStart),
263
94
      (void *)true,
264
94
      stringPrototypePad,
265
94
      1);
266
94
  defineMethod(
267
94
      runtime,
268
94
      stringPrototype,
269
94
      Predefined::getSymbolID(Predefined::replace),
270
94
      ctx,
271
94
      stringPrototypeReplace,
272
94
      2);
273
94
  defineMethod(
274
94
      runtime,
275
94
      stringPrototype,
276
94
      Predefined::getSymbolID(Predefined::search),
277
94
      ctx,
278
94
      stringPrototypeSearch,
279
94
      1);
280
94
  defineMethod(
281
94
      runtime,
282
94
      stringPrototype,
283
94
      Predefined::getSymbolID(Predefined::charAt),
284
94
      ctx,
285
94
      stringPrototypeCharAt,
286
94
      1);
287
94
  defineMethod(
288
94
      runtime,
289
94
      stringPrototype,
290
94
      Predefined::getSymbolID(Predefined::endsWith),
291
94
      ctx,
292
94
      stringPrototypeEndsWith,
293
94
      1);
294
94
  defineMethod(
295
94
      runtime,
296
94
      stringPrototype,
297
94
      Predefined::getSymbolID(Predefined::slice),
298
94
      ctx,
299
94
      stringPrototypeSlice,
300
94
      2);
301
94
  defineMethod(
302
94
      runtime,
303
94
      stringPrototype,
304
94
      Predefined::getSymbolID(Predefined::split),
305
94
      ctx,
306
94
      stringPrototypeSplit,
307
94
      2);
308
94
  defineMethod(
309
94
      runtime,
310
94
      stringPrototype,
311
94
      Predefined::getSymbolID(Predefined::includes),
312
94
      (void *)false,
313
94
      stringPrototypeIncludesOrStartsWith,
314
94
      1);
315
94
  defineMethod(
316
94
      runtime,
317
94
      stringPrototype,
318
94
      Predefined::getSymbolID(Predefined::indexOf),
319
94
      ctx,
320
94
      stringPrototypeIndexOf,
321
94
      1);
322
94
  defineMethod(
323
94
      runtime,
324
94
      stringPrototype,
325
94
      Predefined::getSymbolID(Predefined::lastIndexOf),
326
94
      ctx,
327
94
      stringPrototypeLastIndexOf,
328
94
      1);
329
94
  defineMethod(
330
94
      runtime,
331
94
      stringPrototype,
332
94
      Predefined::getSymbolID(Predefined::startsWith),
333
94
      (void *)true,
334
94
      stringPrototypeIncludesOrStartsWith,
335
94
      1);
336
337
94
  return cons;
338
94
}
339
340
CallResult<HermesValue>
341
0
stringConstructor(void *, Runtime &runtime, NativeArgs args) {
342
0
  if (args.getArgCount() == 0) {
343
0
    return HermesValue::encodeStringValue(
344
0
        runtime.getPredefinedString(Predefined::emptyString));
345
0
  }
346
347
0
  if (!args.isConstructorCall() && args.getArg(0).isSymbol()) {
348
0
    auto str = symbolDescriptiveString(
349
0
        runtime, Handle<SymbolID>::vmcast(args.getArgHandle(0)));
350
0
    if (LLVM_UNLIKELY(str == ExecutionStatus::EXCEPTION)) {
351
0
      return ExecutionStatus::EXCEPTION;
352
0
    }
353
0
    return str->getHermesValue();
354
0
  }
355
356
0
  auto sRes = toString_RJS(runtime, args.getArgHandle(0));
357
0
  if (sRes == ExecutionStatus::EXCEPTION) {
358
0
    return ExecutionStatus::EXCEPTION;
359
0
  }
360
0
  auto s = runtime.makeHandle(std::move(*sRes));
361
362
0
  if (!args.isConstructorCall()) {
363
    // Not a constructor call, just return the string value.
364
0
    return s.getHermesValue();
365
0
  }
366
367
  // Constructor call: initialize the JSString.
368
0
  auto self = args.vmcastThis<JSString>();
369
0
  JSString::setPrimitiveString(self, runtime, s);
370
371
0
  return self.getHermesValue();
372
0
}
373
374
CallResult<HermesValue>
375
0
stringFromCharCode(void *, Runtime &runtime, NativeArgs args) {
376
0
  GCScope gcScope(runtime);
377
0
  uint32_t n = args.getArgCount();
378
0
  if (LLVM_LIKELY(n == 1)) {
379
    // Fast path for when only one argument is provided.
380
0
    auto res = toUInt16(runtime, args.getArgHandle(0));
381
0
    if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
382
0
      return ExecutionStatus::EXCEPTION;
383
0
    }
384
0
    char16_t ch = res->getNumber();
385
0
    return runtime.getCharacterString(ch).getHermesValue();
386
0
  }
387
0
  auto builder = StringBuilder::createStringBuilder(runtime, SafeUInt32{n});
388
0
  if (builder == ExecutionStatus::EXCEPTION) {
389
0
    return ExecutionStatus::EXCEPTION;
390
0
  }
391
0
  for (unsigned i = 0; i < n; ++i) {
392
    // Call a function that may throw, let the runtime record it.
393
0
    auto res = toUInt16(runtime, args.getArgHandle(i));
394
0
    if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
395
0
      return ExecutionStatus::EXCEPTION;
396
0
    }
397
0
    char16_t ch = res->getNumber();
398
0
    builder->appendCharacter(ch);
399
0
  }
400
0
  return HermesValue::encodeStringValue(*builder->getStringPrimitive());
401
0
}
402
403
CallResult<HermesValue>
404
0
stringFromCodePoint(void *, Runtime &runtime, NativeArgs args) {
405
0
  GCScope gcScope{runtime};
406
407
  // 1. Let codePoints be a List containing the arguments passed to this
408
  // function.
409
  // 2. Let length be the number of elements in codePoints.
410
0
  uint32_t length = args.getArgCount();
411
  // 3. Let elements be a new List.
412
0
  llvh::SmallVector<char16_t, 32> elements{};
413
  // 4. Let nextIndex be 0.
414
0
  uint32_t nextIndex = 0;
415
416
0
  MutableHandle<> next{runtime};
417
0
  MutableHandle<> nextCP{runtime};
418
419
0
  GCScopeMarkerRAII marker{gcScope};
420
  // 5. Repeat while nextIndex < length
421
0
  for (; nextIndex < length; marker.flush()) {
422
0
    marker.flush();
423
    // 5a. Let next be codePoints[nextIndex].
424
0
    next = args.getArg(nextIndex);
425
    // 5b. Let nextCP be toNumber_RJS(next).
426
0
    auto nextCPRes = toNumber_RJS(runtime, next);
427
0
    if (LLVM_UNLIKELY(nextCPRes == ExecutionStatus::EXCEPTION)) {
428
0
      return ExecutionStatus::EXCEPTION;
429
0
    }
430
0
    nextCP = *nextCPRes;
431
432
    // 5d. If SameValue(nextCP, ToIntegerOrInfinity(nextCP)) is false, throw
433
    // a RangeError exception.
434
0
    auto nextCPInt = toIntegerOrInfinity(runtime, nextCP);
435
0
    if (LLVM_UNLIKELY(nextCPInt == ExecutionStatus::EXCEPTION)) {
436
0
      return ExecutionStatus::EXCEPTION;
437
0
    }
438
0
    if (!isSameValue(*nextCP, *nextCPInt)) {
439
0
      return runtime.raiseRangeError(
440
0
          TwineChar16("Code point must be an integer: ") + nextCP->getNumber());
441
0
    }
442
443
    // 5e. If nextCP < 0 or nextCP > 0x10FFFF, throw a RangeError exception.
444
0
    if (nextCP->getNumber() < 0 || nextCP->getNumber() > 0x10FFFF) {
445
0
      return runtime.raiseRangeError(
446
0
          TwineChar16("Code point out of bounds: ") + nextCP->getNumber());
447
0
    }
448
449
    // 5f. Append the elements of the UTF16Encoding (10.1.1) of nextCP to the
450
    // end of elements.
451
    // Safe to get as uint32_t because we've done int and bounds checking.
452
0
    utf16Encoding(nextCP->getNumberAs<uint32_t>(), elements);
453
454
    // 5g. Let nextIndex be nextIndex + 1.
455
0
    ++nextIndex;
456
0
  }
457
458
  // 6. Return the String value whose elements are, in order, the elements in
459
  // the List elements. If length is 0, the empty string is returned.
460
0
  return StringPrimitive::createEfficient(runtime, elements);
461
0
}
462
463
/// ES6.0 21.1.2.4 String.raw ( template , ...substitutions )
464
0
CallResult<HermesValue> stringRaw(void *, Runtime &runtime, NativeArgs args) {
465
0
  GCScope gcScope{runtime};
466
467
  // 1. Let substitutions be a List consisting of all of the arguments passed to
468
  // this function, starting with the second argument.
469
  // If fewer than two arguments were passed, the List is empty.
470
  // 2. Let numberOfSubstitutions be the number of elements in substitutions.
471
0
  uint32_t numberOfSubstitutions =
472
0
      args.getArgCount() < 2 ? 0 : args.getArgCount() - 1;
473
474
  // 3. Let cooked be ToObject(template).
475
0
  auto cookedRes = toObject(runtime, args.getArgHandle(0));
476
0
  if (LLVM_UNLIKELY(cookedRes == ExecutionStatus::EXCEPTION)) {
477
0
    return ExecutionStatus::EXCEPTION;
478
0
  }
479
0
  auto cooked = runtime.makeHandle<JSObject>(*cookedRes);
480
481
  // 5. Let raw be ToObject(Get(cooked, "raw")).
482
0
  auto getRes = JSObject::getNamed_RJS(
483
0
      cooked, runtime, Predefined::getSymbolID(Predefined::raw));
484
0
  if (LLVM_UNLIKELY(getRes == ExecutionStatus::EXCEPTION)) {
485
0
    return ExecutionStatus::EXCEPTION;
486
0
  }
487
0
  auto rawRes = toObject(runtime, runtime.makeHandle(std::move(*getRes)));
488
0
  if (LLVM_UNLIKELY(rawRes == ExecutionStatus::EXCEPTION)) {
489
0
    return ExecutionStatus::EXCEPTION;
490
0
  }
491
0
  auto raw = runtime.makeHandle<JSObject>(*rawRes);
492
493
  // 7. Let literalSegments be ToLength(Get(raw, "length"))
494
0
  auto lengthRes = JSObject::getNamed_RJS(
495
0
      raw, runtime, Predefined::getSymbolID(Predefined::length));
496
0
  if (LLVM_UNLIKELY(lengthRes == ExecutionStatus::EXCEPTION)) {
497
0
    return ExecutionStatus::EXCEPTION;
498
0
  }
499
0
  auto literalSegmentsRes =
500
0
      toLength(runtime, runtime.makeHandle(std::move(*lengthRes)));
501
0
  if (LLVM_UNLIKELY(literalSegmentsRes == ExecutionStatus::EXCEPTION)) {
502
0
    return ExecutionStatus::EXCEPTION;
503
0
  }
504
0
  int64_t literalSegments = literalSegmentsRes->getNumberAs<int64_t>();
505
  // 9. If literalSegments ≤ 0, return the empty string.
506
0
  if (literalSegments <= 0) {
507
0
    return HermesValue::encodeStringValue(
508
0
        runtime.getPredefinedString(Predefined::emptyString));
509
0
  }
510
511
  // 10. Let stringElements be a new List.
512
0
  llvh::SmallVector<char16_t, 32> stringElements{};
513
514
  // 11. Let nextIndex be 0.
515
0
  MutableHandle<> nextIndex{
516
0
      runtime, HermesValue::encodeUntrustedNumberValue(0)};
517
518
0
  MutableHandle<> tmpHandle{runtime};
519
0
  MutableHandle<StringPrimitive> nextSeg{runtime};
520
0
  MutableHandle<> next{runtime};
521
0
  MutableHandle<StringPrimitive> nextSub{runtime};
522
523
  // 12. Repeat
524
0
  GCScopeMarkerRAII marker{gcScope};
525
0
  for (;; marker.flush()) {
526
    // 12. a. Let nextKey be ToString(nextIndex).
527
    // 12. b. Let nextSeg be ToString(Get(raw, nextKey)).
528
0
    auto nextSegPropRes = JSObject::getComputed_RJS(raw, runtime, nextIndex);
529
0
    if (LLVM_UNLIKELY(nextSegPropRes == ExecutionStatus::EXCEPTION)) {
530
0
      return ExecutionStatus::EXCEPTION;
531
0
    }
532
0
    tmpHandle = std::move(*nextSegPropRes);
533
0
    auto nextSegRes = toString_RJS(runtime, tmpHandle);
534
0
    if (LLVM_UNLIKELY(nextSegRes == ExecutionStatus::EXCEPTION)) {
535
0
      return ExecutionStatus::EXCEPTION;
536
0
    }
537
0
    nextSeg = nextSegRes->get();
538
539
    // 12. d. Append in order the code unit elements of nextSeg to the end of
540
    // stringElements.
541
0
    nextSeg->appendUTF16String(stringElements);
542
543
    // 12. e. If nextIndex + 1 = literalSegments, then
544
0
    if (nextIndex->getNumberAs<int64_t>() + 1 == literalSegments) {
545
      // 12. i. Return the String value whose code units are, in order, the
546
      // elements in the List stringElements. If stringElements has no elements,
547
      // the empty string is returned.
548
0
      return StringPrimitive::createEfficient(runtime, stringElements);
549
0
    }
550
551
0
    if (nextIndex->getNumberAs<int64_t>() < numberOfSubstitutions) {
552
      // 12. f. If nextIndex < numberOfSubstitutions, let next be
553
      // substitutions[nextIndex].
554
      // Add one to nextIndex to get index in substitutions.
555
0
      next = args.getArg(nextIndex->getNumberAs<int64_t>() + 1);
556
      // 12. h. Let nextSub be ToString(next).
557
0
      auto nextSubRes = toString_RJS(runtime, next);
558
0
      if (LLVM_UNLIKELY(nextSubRes == ExecutionStatus::EXCEPTION)) {
559
0
        return ExecutionStatus::EXCEPTION;
560
0
      }
561
0
      nextSub = nextSubRes->get();
562
      // 12. j. Append in order the code unit elements of nextSub to the end of
563
      // stringElements.
564
0
      nextSub->appendUTF16String(stringElements);
565
0
    }
566
567
    // 12. g. Else, let next be the empty String.
568
    // Omitted because nothing happens.
569
570
    // 12. k. Let nextIndex be nextIndex + 1.
571
0
    nextIndex = HermesValue::encodeUntrustedNumberValue(
572
0
        nextIndex->getNumberAs<int64_t>() + 1);
573
0
  }
574
0
}
575
576
//===----------------------------------------------------------------------===//
577
/// String.prototype.
578
579
/// 22.1.3.1
580
CallResult<HermesValue>
581
0
stringPrototypeAt(void *, Runtime &runtime, NativeArgs args) {
582
0
  GCScope gcScope(runtime);
583
  // 1. Let O be RequireObjectCoercible(this value).
584
0
  if (LLVM_UNLIKELY(
585
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
586
0
          ExecutionStatus::EXCEPTION)) {
587
0
    return ExecutionStatus::EXCEPTION;
588
0
  }
589
590
  // 2. Let S be ToString(O).
591
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
592
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
593
0
    return ExecutionStatus::EXCEPTION;
594
0
  }
595
0
  auto S = runtime.makeHandle(std::move(*strRes));
596
597
  // 3. Let len be the length of S.
598
0
  double len = S->getStringLength();
599
600
  // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
601
0
  auto idx = args.getArgHandle(0);
602
0
  auto relativeIndexRes = toIntegerOrInfinity(runtime, idx);
603
0
  if (LLVM_UNLIKELY(relativeIndexRes == ExecutionStatus::EXCEPTION)) {
604
0
    return ExecutionStatus::EXCEPTION;
605
0
  }
606
0
  const double relativeIndex = relativeIndexRes->getNumber();
607
608
0
  double k;
609
  // 5. If relativeIndex ≥ 0, then
610
0
  if (relativeIndex >= 0) {
611
    // a. Let k be relativeIndex.
612
0
    k = relativeIndex;
613
0
  } else {
614
    // 6. Else,
615
    // a. Let k be len + relativeIndex.
616
0
    k = len + relativeIndex;
617
0
  }
618
619
  // 6. If k < 0 or k ≥ len, return undefined.
620
0
  if (k < 0 || k >= len) {
621
0
    return HermesValue::encodeUndefinedValue();
622
0
  }
623
624
  // 8. Return the substring of S from k to k + 1.
625
0
  auto sliceRes = StringPrimitive::slice(runtime, S, k, 1);
626
0
  if (LLVM_UNLIKELY(sliceRes == ExecutionStatus::EXCEPTION)) {
627
0
    return ExecutionStatus::EXCEPTION;
628
0
  }
629
0
  return sliceRes;
630
0
}
631
632
CallResult<HermesValue>
633
0
stringPrototypeToString(void *, Runtime &runtime, NativeArgs args) {
634
0
  if (args.getThisArg().isString()) {
635
0
    return args.getThisArg();
636
0
  }
637
638
  // Not a String value, must be a string object.
639
0
  auto *strPtr = dyn_vmcast<JSString>(args.getThisArg());
640
0
  if (strPtr) {
641
    // Only return the string if called on a String object.
642
0
    return HermesValue::encodeStringValue(
643
0
        JSString::getPrimitiveString(strPtr, runtime));
644
0
  }
645
0
  return runtime.raiseTypeError(
646
0
      "String.prototype.toString() called on non-string object");
647
0
}
648
649
CallResult<HermesValue>
650
0
stringPrototypeCharCodeAt(void *, Runtime &runtime, NativeArgs args) {
651
0
  Handle<> thisValue{&args.getThisArg()};
652
  // Call a function that may throw, let the runtime record it.
653
0
  if (LLVM_UNLIKELY(
654
0
          checkObjectCoercible(runtime, thisValue) ==
655
0
          ExecutionStatus::EXCEPTION)) {
656
0
    return ExecutionStatus::EXCEPTION;
657
0
  }
658
0
  auto strRes = toString_RJS(runtime, thisValue);
659
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
660
0
    return ExecutionStatus::EXCEPTION;
661
0
  }
662
0
  auto S = runtime.makeHandle(std::move(*strRes));
663
0
  auto intRes =
664
0
      toIntegerOrInfinity(runtime, runtime.makeHandle(args.getArg(0)));
665
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
666
0
    return ExecutionStatus::EXCEPTION;
667
0
  }
668
0
  auto position = intRes->getNumber();
669
0
  auto size = S->getStringLength();
670
0
  if (position < 0 || position >= size) {
671
0
    return HermesValue::encodeNaNValue();
672
0
  }
673
0
  return HermesValue::encodeTrustedNumberValue(
674
0
      StringPrimitive::createStringView(runtime, S)[position]);
675
0
}
676
677
CallResult<HermesValue>
678
0
stringPrototypeCodePointAt(void *, Runtime &runtime, NativeArgs args) {
679
  // 1. Let O be RequireObjectCoercible(this value).
680
0
  if (LLVM_UNLIKELY(
681
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
682
0
          ExecutionStatus::EXCEPTION)) {
683
0
    return ExecutionStatus::EXCEPTION;
684
0
  }
685
686
  // 2. Let S be ToString(O).
687
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
688
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
689
0
    return ExecutionStatus::EXCEPTION;
690
0
  }
691
0
  auto S = runtime.makeHandle(std::move(*strRes));
692
693
  // 4. Let position be ToIntegerOrInfinity(pos).
694
0
  auto positionRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
695
0
  if (LLVM_UNLIKELY(positionRes == ExecutionStatus::EXCEPTION)) {
696
0
    return ExecutionStatus::EXCEPTION;
697
0
  }
698
0
  double position = positionRes->getNumber();
699
700
  // 6. Let size be the number of elements in S.
701
0
  double size = S->getStringLength();
702
703
  // 7. If position < 0 or position ≥ size, return undefined.
704
0
  if (position < 0 || position >= size) {
705
0
    return HermesValue::encodeUndefinedValue();
706
0
  }
707
708
0
  auto strView = StringPrimitive::createStringView(runtime, S);
709
710
  // 8. Let first be the code unit value of the element at index position in the
711
  // String S.
712
0
  char16_t first = strView[position];
713
714
  // 9. If first < 0xD800 or first > 0xDBFF or position+1 = size, return first.
715
0
  if (first < 0xD800 || first > 0xDBFF || position + 1 == size) {
716
0
    return HermesValue::encodeUntrustedNumberValue(first);
717
0
  }
718
719
  // 10. Let second be the code unit value of the element at index position+1 in
720
  // the String S.
721
  // Safe to access because we ensured that position + 1 < size.
722
0
  char16_t second = strView[position + 1];
723
724
  // 11. If second < 0xDC00 or second > 0xDFFF, return first.
725
0
  if (second < 0xDC00 || second > 0xDFFF) {
726
0
    return HermesValue::encodeUntrustedNumberValue(first);
727
0
  }
728
729
  // 12. Return UTF16Decode(first, second).
730
0
  return HermesValue::encodeUntrustedNumberValue(utf16Decode(first, second));
731
0
}
732
733
CallResult<HermesValue>
734
0
stringPrototypeConcat(void *, Runtime &runtime, NativeArgs args) {
735
0
  GCScope gcScope(runtime);
736
737
0
  if (LLVM_UNLIKELY(
738
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
739
0
          ExecutionStatus::EXCEPTION)) {
740
0
    return ExecutionStatus::EXCEPTION;
741
0
  }
742
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
743
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
744
0
    return ExecutionStatus::EXCEPTION;
745
0
  }
746
0
  auto S = runtime.makeHandle(std::move(*strRes));
747
  // Track the total characters in the result.
748
0
  SafeUInt32 size(S->getStringLength());
749
0
  uint32_t argCount = args.getArgCount();
750
751
  // Store the results of toStrings and concat them at the end.
752
0
  auto arrRes = ArrayStorageSmall::create(runtime, argCount, argCount);
753
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
754
0
    return ExecutionStatus::EXCEPTION;
755
0
  }
756
0
  auto strings = runtime.makeHandle<ArrayStorageSmall>(*arrRes);
757
758
  // Run toString on the arguments to figure out the final size.
759
0
  auto marker = gcScope.createMarker();
760
0
  for (uint32_t i = 0; i < argCount; ++i) {
761
0
    auto strRes = toString_RJS(runtime, args.getArgHandle(i));
762
0
    if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
763
0
      return ExecutionStatus::EXCEPTION;
764
0
    }
765
766
    // Allocations can't be performed here,
767
    // and we know we're in bounds because we preallocated.
768
0
    strings->set(
769
0
        i,
770
0
        SmallHermesValue::encodeStringValue(strRes->get(), runtime),
771
0
        runtime.getHeap());
772
0
    uint32_t strLength = strRes->get()->getStringLength();
773
774
0
    size.add(strLength);
775
0
    if (LLVM_UNLIKELY(size.isOverflowed())) {
776
0
      return runtime.raiseRangeError("resulting string length exceeds limit");
777
0
    }
778
779
0
    gcScope.flushToMarker(marker);
780
0
  }
781
782
  // Allocate the complete result.
783
0
  auto builder = StringBuilder::createStringBuilder(runtime, size);
784
0
  if (builder == ExecutionStatus::EXCEPTION) {
785
0
    return ExecutionStatus::EXCEPTION;
786
0
  }
787
788
  // Copy 'this' argument first.
789
0
  builder->appendStringPrim(S);
790
0
  MutableHandle<StringPrimitive> element{runtime};
791
792
  // Copy the rest of the strings.
793
0
  for (uint32_t i = 0; i < argCount; i++) {
794
0
    element = strings->at(i).getString(runtime);
795
0
    builder->appendStringPrim(element);
796
0
  }
797
0
  return builder->getStringPrimitive().getHermesValue();
798
0
}
799
800
CallResult<HermesValue>
801
0
stringPrototypeSubstring(void *, Runtime &runtime, NativeArgs args) {
802
0
  if (LLVM_UNLIKELY(
803
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
804
0
          ExecutionStatus::EXCEPTION)) {
805
0
    return ExecutionStatus::EXCEPTION;
806
0
  }
807
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
808
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
809
0
    return ExecutionStatus::EXCEPTION;
810
0
  }
811
0
  auto S = runtime.makeHandle(std::move(*strRes));
812
0
  double len = S->getStringLength();
813
814
0
  auto intRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
815
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
816
0
    return ExecutionStatus::EXCEPTION;
817
0
  }
818
0
  double intStart = intRes->getNumber();
819
820
0
  double intEnd;
821
0
  if (args.getArg(1).isUndefined()) {
822
0
    intEnd = len;
823
0
  } else {
824
0
    if (LLVM_UNLIKELY(
825
0
            (intRes = toIntegerOrInfinity(runtime, args.getArgHandle(1))) ==
826
0
            ExecutionStatus::EXCEPTION)) {
827
0
      return ExecutionStatus::EXCEPTION;
828
0
    }
829
0
    intEnd = intRes->getNumber();
830
0
  }
831
832
0
  size_t finalStart = std::min(std::max(intStart, 0.0), len);
833
0
  size_t finalEnd = std::min(std::max(intEnd, 0.0), len);
834
0
  size_t from = std::min(finalStart, finalEnd);
835
0
  size_t to = std::max(finalStart, finalEnd);
836
837
0
  return StringPrimitive::slice(runtime, S, from, to > from ? to - from : 0);
838
0
}
839
840
static CallResult<HermesValue> convertCase(
841
    Runtime &runtime,
842
    Handle<StringPrimitive> S,
843
    const bool upperCase,
844
0
    const bool useCurrentLocale) {
845
  // Copying is unavoidable in this function, do it early on.
846
0
  SmallU16String<32> buff;
847
  // Must copy instead of just getting the reference, because later operations
848
  // may trigger GC and hence invalid pointers inside S.
849
0
  S->appendUTF16String(buff);
850
0
  UTF16Ref str = buff.arrayRef();
851
852
0
  if (!useCurrentLocale) {
853
    // Try a fast path for ASCII strings.
854
    // First, bitwise-or all the characters to see if any one isn't ASCII.
855
0
    char16_t mask = 0;
856
    // Also, check if we have to do work or we can just return S directly.
857
0
    bool noop = true;
858
0
    if (upperCase) {
859
0
      for (const auto c : str) {
860
0
        mask |= c;
861
        // It's still a noop if the character isn't a lowercase ASCII.
862
0
        noop &= !('a' <= c && c <= 'z');
863
0
      }
864
0
    } else {
865
0
      for (const auto c : str) {
866
0
        mask |= c;
867
        // It's still a noop if the character isn't an uppercase ASCII.
868
0
        noop &= !('A' <= c && c <= 'Z');
869
0
      }
870
0
    }
871
0
    if (mask <= 127) {
872
0
      if (noop) {
873
        // We don't have to allocate anything.
874
0
        return S.getHermesValue();
875
0
      }
876
877
0
      if (str.size() == 1) {
878
        // Use the Runtime stored representations of single-character strings.
879
0
        char16_t c = str[0];
880
0
        if (upperCase) {
881
0
          char16_t isLower = 'a' <= c && c <= 'z';
882
0
          return runtime.getCharacterString(c & ~(isLower << 5))
883
0
              .getHermesValue();
884
0
        } else {
885
0
          char16_t isUpper = 'A' <= c && c <= 'Z';
886
0
          return runtime.getCharacterString(c | (isUpper << 5))
887
0
              .getHermesValue();
888
0
        }
889
0
      }
890
891
0
      SafeUInt32 len(S->getStringLength());
892
0
      auto builder = StringBuilder::createStringBuilder(runtime, len);
893
0
      if (builder == ExecutionStatus::EXCEPTION) {
894
0
        return ExecutionStatus::EXCEPTION;
895
0
      }
896
0
      char16_t ch;
897
0
      if (upperCase) {
898
0
        for (const char16_t c : str) {
899
          // If it is lower, then clear the 5th bit, else do nothing.
900
0
          char16_t isLower = 'a' <= c && c <= 'z';
901
0
          ch = c & ~(isLower << 5);
902
0
          builder->appendCharacter(ch);
903
0
        }
904
0
      } else {
905
0
        for (const char16_t c : str) {
906
          // If it is upper, then set the 5th bit, else do nothing.
907
0
          char16_t isUpper = 'A' <= c && c <= 'Z';
908
0
          ch = c | (isUpper << 5);
909
0
          builder->appendCharacter(ch);
910
0
        }
911
0
      }
912
0
      return HermesValue::encodeStringValue(*builder->getStringPrimitive());
913
0
    }
914
0
  }
915
0
  platform_unicode::convertToCase(
916
0
      buff,
917
0
      upperCase ? platform_unicode::CaseConversion::ToUpper
918
0
                : platform_unicode::CaseConversion::ToLower,
919
0
      useCurrentLocale);
920
0
  return StringPrimitive::create(runtime, buff);
921
0
}
922
923
CallResult<HermesValue>
924
0
stringPrototypeToLowerCase(void *, Runtime &runtime, NativeArgs args) {
925
0
  if (LLVM_UNLIKELY(
926
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
927
0
          ExecutionStatus::EXCEPTION)) {
928
0
    return ExecutionStatus::EXCEPTION;
929
0
  }
930
0
  auto res = toString_RJS(runtime, args.getThisHandle());
931
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
932
0
    return ExecutionStatus::EXCEPTION;
933
0
  }
934
0
  return convertCase(
935
0
      runtime, runtime.makeHandle(std::move(*res)), false, false);
936
0
}
937
938
CallResult<HermesValue>
939
0
stringPrototypeToLocaleLowerCase(void *ctx, Runtime &runtime, NativeArgs args) {
940
#ifdef HERMES_ENABLE_INTL
941
  return intlStringPrototypeToLocaleLowerCase(/* unused */ ctx, runtime, args);
942
#else
943
0
  if (LLVM_UNLIKELY(
944
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
945
0
          ExecutionStatus::EXCEPTION)) {
946
0
    return ExecutionStatus::EXCEPTION;
947
0
  }
948
0
  auto res = toString_RJS(runtime, args.getThisHandle());
949
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
950
0
    return ExecutionStatus::EXCEPTION;
951
0
  }
952
0
  return convertCase(runtime, runtime.makeHandle(std::move(*res)), false, true);
953
0
#endif
954
0
}
955
956
CallResult<HermesValue>
957
0
stringPrototypeToUpperCase(void *, Runtime &runtime, NativeArgs args) {
958
0
  if (LLVM_UNLIKELY(
959
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
960
0
          ExecutionStatus::EXCEPTION)) {
961
0
    return ExecutionStatus::EXCEPTION;
962
0
  }
963
0
  auto res = toString_RJS(runtime, args.getThisHandle());
964
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
965
0
    return ExecutionStatus::EXCEPTION;
966
0
  }
967
0
  return convertCase(runtime, runtime.makeHandle(std::move(*res)), true, false);
968
0
}
969
970
CallResult<HermesValue>
971
0
stringPrototypeToLocaleUpperCase(void *ctx, Runtime &runtime, NativeArgs args) {
972
#ifdef HERMES_ENABLE_INTL
973
  return intlStringPrototypeToLocaleUpperCase(/* unused */ ctx, runtime, args);
974
#else
975
0
  if (LLVM_UNLIKELY(
976
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
977
0
          ExecutionStatus::EXCEPTION)) {
978
0
    return ExecutionStatus::EXCEPTION;
979
0
  }
980
0
  auto res = toString_RJS(runtime, args.getThisHandle());
981
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
982
0
    return ExecutionStatus::EXCEPTION;
983
0
  }
984
0
  return convertCase(runtime, runtime.makeHandle(std::move(*res)), true, true);
985
0
#endif
986
0
}
987
988
CallResult<HermesValue>
989
0
stringPrototypeSubstr(void *, Runtime &runtime, NativeArgs args) {
990
0
  if (LLVM_UNLIKELY(
991
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
992
0
          ExecutionStatus::EXCEPTION)) {
993
0
    return ExecutionStatus::EXCEPTION;
994
0
  }
995
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
996
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
997
0
    return ExecutionStatus::EXCEPTION;
998
0
  }
999
0
  auto S = runtime.makeHandle(std::move(*strRes));
1000
0
  double stringLen = S->getStringLength();
1001
1002
0
  auto intRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
1003
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1004
0
    return ExecutionStatus::EXCEPTION;
1005
0
  }
1006
0
  double start = intRes->getNumber();
1007
1008
0
  double length;
1009
0
  if (args.getArg(1).isUndefined()) {
1010
0
    length = stringLen;
1011
0
  } else {
1012
0
    if (LLVM_UNLIKELY(
1013
0
            (intRes = toIntegerOrInfinity(runtime, args.getArgHandle(1))) ==
1014
0
            ExecutionStatus::EXCEPTION)) {
1015
0
      return ExecutionStatus::EXCEPTION;
1016
0
    }
1017
0
    length = intRes->getNumber();
1018
0
  }
1019
1020
0
  if (start < 0) {
1021
0
    start = std::max(stringLen + start, 0.0);
1022
0
  }
1023
0
  double adjustedLength = std::min(std::max(length, 0.0), stringLen - start);
1024
0
  if (adjustedLength <= 0) {
1025
0
    return HermesValue::encodeStringValue(
1026
0
        runtime.getPredefinedString(Predefined::emptyString));
1027
0
  } else {
1028
0
    return StringPrimitive::slice(
1029
0
        runtime,
1030
0
        S,
1031
0
        static_cast<size_t>(start),
1032
0
        static_cast<size_t>(adjustedLength));
1033
0
  }
1034
0
}
1035
1036
/// \return the number of characters to trim from the begin iterator.
1037
static size_t trimStart(
1038
    StringView::const_iterator begin,
1039
0
    StringView::const_iterator end) {
1040
0
  size_t toTrim = 0;
1041
0
  while (begin != end &&
1042
0
         (isWhiteSpaceChar(*begin) || isLineTerminatorChar(*begin))) {
1043
0
    ++begin;
1044
0
    ++toTrim;
1045
0
  }
1046
0
  return toTrim;
1047
0
}
1048
1049
/// \return the number of characters to trim from the end iterator.
1050
static size_t trimEnd(
1051
    StringView::const_iterator begin,
1052
0
    StringView::const_iterator end) {
1053
0
  size_t toTrim = 0;
1054
0
  while (begin != end &&
1055
0
         (isWhiteSpaceChar(*(end - 1)) || isLineTerminatorChar(*(end - 1)))) {
1056
0
    --end;
1057
0
    ++toTrim;
1058
0
  }
1059
0
  return toTrim;
1060
0
}
1061
1062
CallResult<HermesValue>
1063
0
stringPrototypeTrim(void *, Runtime &runtime, NativeArgs args) {
1064
0
  if (LLVM_UNLIKELY(
1065
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
1066
0
          ExecutionStatus::EXCEPTION)) {
1067
0
    return ExecutionStatus::EXCEPTION;
1068
0
  }
1069
0
  auto res = toString_RJS(runtime, args.getThisHandle());
1070
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
1071
0
    return ExecutionStatus::EXCEPTION;
1072
0
  }
1073
0
  auto S = runtime.makeHandle(std::move(*res));
1074
1075
  // Move begin and end to point to the first and last non-whitespace chars.
1076
0
  size_t beginIdx = 0, endIdx = S->getStringLength();
1077
0
  {
1078
0
    auto str = StringPrimitive::createStringView(runtime, S);
1079
0
    auto begin = str.begin();
1080
0
    auto end = str.end();
1081
0
    beginIdx = trimStart(begin, end);
1082
0
    begin += beginIdx;
1083
0
    endIdx -= trimEnd(begin, end);
1084
0
  }
1085
1086
0
  return StringPrimitive::slice(runtime, S, beginIdx, endIdx - beginIdx);
1087
0
}
1088
1089
CallResult<HermesValue>
1090
0
stringPrototypeTrimStart(void *, Runtime &runtime, NativeArgs args) {
1091
0
  if (LLVM_UNLIKELY(
1092
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
1093
0
          ExecutionStatus::EXCEPTION)) {
1094
0
    return ExecutionStatus::EXCEPTION;
1095
0
  }
1096
0
  auto res = toString_RJS(runtime, args.getThisHandle());
1097
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
1098
0
    return ExecutionStatus::EXCEPTION;
1099
0
  }
1100
0
  auto S = runtime.makeHandle(std::move(*res));
1101
1102
  // Move begin and end to point to the first and last non-whitespace chars.
1103
0
  size_t beginIdx = 0;
1104
0
  {
1105
0
    auto str = StringPrimitive::createStringView(runtime, S);
1106
0
    auto begin = str.begin();
1107
0
    auto end = str.end();
1108
0
    beginIdx = trimStart(begin, end);
1109
0
  }
1110
1111
0
  return StringPrimitive::slice(
1112
0
      runtime, S, beginIdx, S->getStringLength() - beginIdx);
1113
0
}
1114
1115
CallResult<HermesValue>
1116
0
stringPrototypeTrimEnd(void *, Runtime &runtime, NativeArgs args) {
1117
0
  if (LLVM_UNLIKELY(
1118
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
1119
0
          ExecutionStatus::EXCEPTION)) {
1120
0
    return ExecutionStatus::EXCEPTION;
1121
0
  }
1122
0
  auto res = toString_RJS(runtime, args.getThisHandle());
1123
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
1124
0
    return ExecutionStatus::EXCEPTION;
1125
0
  }
1126
0
  auto S = runtime.makeHandle(std::move(*res));
1127
1128
  // Move begin and end to point to the first and last non-whitespace chars.
1129
0
  size_t endIdx = S->getStringLength();
1130
0
  {
1131
0
    auto str = StringPrimitive::createStringView(runtime, S);
1132
0
    auto begin = str.begin();
1133
0
    auto end = str.end();
1134
0
    endIdx -= trimEnd(begin, end);
1135
0
  }
1136
1137
0
  return StringPrimitive::slice(runtime, S, 0, endIdx);
1138
0
}
1139
1140
CallResult<HermesValue>
1141
0
stringPrototypeLocaleCompare(void *ctx, Runtime &runtime, NativeArgs args) {
1142
#ifdef HERMES_ENABLE_INTL
1143
  return intlStringPrototypeLocaleCompare(/* unused */ ctx, runtime, args);
1144
#else
1145
0
  auto thisValue = args.getThisHandle();
1146
0
  if (LLVM_UNLIKELY(
1147
0
          checkObjectCoercible(runtime, thisValue) ==
1148
0
          ExecutionStatus::EXCEPTION)) {
1149
0
    return ExecutionStatus::EXCEPTION;
1150
0
  }
1151
0
  auto sRes = toString_RJS(runtime, thisValue);
1152
0
  if (LLVM_UNLIKELY(sRes == ExecutionStatus::EXCEPTION)) {
1153
0
    return ExecutionStatus::EXCEPTION;
1154
0
  }
1155
0
  auto S = runtime.makeHandle(std::move(*sRes));
1156
1157
0
  auto tRes = toString_RJS(runtime, args.getArgHandle(0));
1158
0
  if (LLVM_UNLIKELY(tRes == ExecutionStatus::EXCEPTION)) {
1159
0
    return ExecutionStatus::EXCEPTION;
1160
0
  }
1161
  // "That" string.
1162
0
  auto T = runtime.makeHandle(std::move(*tRes));
1163
1164
0
  llvh::SmallVector<char16_t, 32> left;
1165
0
  llvh::SmallVector<char16_t, 32> right;
1166
1167
0
  StringPrimitive::createStringView(runtime, S).appendUTF16String(left);
1168
0
  StringPrimitive::createStringView(runtime, T).appendUTF16String(right);
1169
0
  int comparisonResult = platform_unicode::localeCompare(left, right);
1170
0
  assert(comparisonResult >= -1 && comparisonResult <= 1);
1171
0
  return HermesValue::encodeUntrustedNumberValue(comparisonResult);
1172
0
#endif
1173
0
}
1174
1175
CallResult<HermesValue>
1176
0
stringPrototypeNormalize(void *, Runtime &runtime, NativeArgs args) {
1177
0
  using platform_unicode::NormalizationForm;
1178
1179
  // 1. Let O be RequireObjectCoercible(this value).
1180
0
  auto O = args.getThisHandle();
1181
0
  if (LLVM_UNLIKELY(
1182
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
1183
0
    return ExecutionStatus::EXCEPTION;
1184
0
  }
1185
  // 2. Let S be ToString(O).
1186
0
  auto sRes = toString_RJS(runtime, O);
1187
0
  if (LLVM_UNLIKELY(sRes == ExecutionStatus::EXCEPTION)) {
1188
0
    return ExecutionStatus::EXCEPTION;
1189
0
  }
1190
0
  auto S = runtime.makeHandle(std::move(*sRes));
1191
1192
0
  NormalizationForm form;
1193
1194
  // 4. If form is not provided or form is undefined, let form be "NFC".
1195
0
  if (args.getArg(0).isUndefined()) {
1196
0
    form = NormalizationForm::C;
1197
0
  } else {
1198
    // 5. Let f be ToString(form).
1199
0
    auto fRes = toString_RJS(runtime, args.getArgHandle(0));
1200
0
    if (LLVM_UNLIKELY(fRes == ExecutionStatus::EXCEPTION)) {
1201
0
      return ExecutionStatus::EXCEPTION;
1202
0
    }
1203
0
    auto f = runtime.makeHandle(std::move(*fRes));
1204
1205
    // 7. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", throw a RangeError
1206
    // exception.
1207
0
    auto sv = StringPrimitive::createStringView(runtime, f);
1208
0
    if (sv.equals(ASCIIRef{"NFC", 3})) {
1209
0
      form = NormalizationForm::C;
1210
0
    } else if (sv.equals(ASCIIRef{"NFD", 3})) {
1211
0
      form = NormalizationForm::D;
1212
0
    } else if (sv.equals(ASCIIRef{"NFKC", 4})) {
1213
0
      form = NormalizationForm::KC;
1214
0
    } else if (sv.equals(ASCIIRef{"NFKD", 4})) {
1215
0
      form = NormalizationForm::KD;
1216
0
    } else {
1217
0
      return runtime.raiseRangeError(
1218
0
          TwineChar16("Invalid normalization form: ") + *f);
1219
0
    }
1220
0
  }
1221
1222
  // 8. Let ns be the String value that is the result of normalizing S into the
1223
  // normalization form named by f as specified in
1224
  // http://www.unicode.org/reports/tr15/tr15-29.html.
1225
0
  llvh::SmallVector<char16_t, 32> ns;
1226
0
  S->appendUTF16String(ns);
1227
0
  platform_unicode::normalize(ns, form);
1228
1229
  // 9. Return ns.
1230
0
  return StringPrimitive::createEfficient(runtime, ns);
1231
0
}
1232
1233
CallResult<HermesValue>
1234
0
stringPrototypeRepeat(void *, Runtime &runtime, NativeArgs args) {
1235
  // 1. Let O be RequireObjectCoercible(this value).
1236
0
  auto O = args.getThisHandle();
1237
0
  if (LLVM_UNLIKELY(
1238
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
1239
0
    return ExecutionStatus::EXCEPTION;
1240
0
  }
1241
  // 2. Let S be ToString(O).
1242
  // 3. ReturnIfAbrupt(S).
1243
0
  auto sRes = toString_RJS(runtime, O);
1244
0
  if (LLVM_UNLIKELY(sRes == ExecutionStatus::EXCEPTION)) {
1245
0
    return ExecutionStatus::EXCEPTION;
1246
0
  }
1247
0
  auto S = runtime.makeHandle(std::move(*sRes));
1248
1249
  // 4. Let n be ToIntegerOrInfinity(count).
1250
  // 5. ReturnIfAbrupt(n).
1251
0
  auto nRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
1252
0
  if (LLVM_UNLIKELY(nRes == ExecutionStatus::EXCEPTION)) {
1253
0
    return ExecutionStatus::EXCEPTION;
1254
0
  }
1255
0
  double n = nRes->getNumber();
1256
1257
  // 6. If n < 0, throw a RangeError exception.
1258
  // 7. If n is +Infinity, throw a RangeError exception.
1259
0
  if (n < 0 || n == std::numeric_limits<double>::infinity()) {
1260
0
    return runtime.raiseRangeError(
1261
0
        "String.prototype.repeat count must be finite and non-negative");
1262
0
  }
1263
1264
  // 8. Let T be a String value that is made from n copies of S appended
1265
  // together. If n is 0, T is the empty String.
1266
0
  double strLen = S->getStringLength();
1267
1268
0
  if (n == 0 || strLen == 0) {
1269
0
    return HermesValue::encodeStringValue(
1270
0
        runtime.getPredefinedString(Predefined::emptyString));
1271
0
  }
1272
1273
0
  if (n > std::numeric_limits<uint32_t>::max() ||
1274
0
      S->getStringLength() > (double)StringPrimitive::MAX_STRING_LENGTH / n) {
1275
    // Check for overflow.
1276
0
    return runtime.raiseRangeError(
1277
0
        "String.prototype.repeat result exceeds limit");
1278
0
  }
1279
1280
  // It's safe to multiply as the overflow check is done above.
1281
0
  SafeUInt32 finalLen(strLen * n);
1282
1283
0
  auto builderRes = StringBuilder::createStringBuilder(runtime, finalLen);
1284
0
  if (LLVM_UNLIKELY(builderRes == ExecutionStatus::EXCEPTION)) {
1285
0
    return ExecutionStatus::EXCEPTION;
1286
0
  }
1287
1288
  // Using uint32_t for i is valid because we have bounds-checked n.
1289
0
  for (uint32_t i = 0; i < n; ++i) {
1290
0
    builderRes->appendStringPrim(S);
1291
0
  }
1292
1293
  // 9. Return T.
1294
0
  return builderRes->getStringPrimitive().getHermesValue();
1295
0
}
1296
1297
/// ES6.0 21.1.3.27 String.prototype [ @@iterator ]( )
1298
CallResult<HermesValue>
1299
1
stringPrototypeSymbolIterator(void *, Runtime &runtime, NativeArgs args) {
1300
  // 1. Let O be RequireObjectCoercible(this value).
1301
1
  auto thisValue = args.getThisHandle();
1302
1
  if (LLVM_UNLIKELY(
1303
1
          checkObjectCoercible(runtime, thisValue) ==
1304
1
          ExecutionStatus::EXCEPTION)) {
1305
0
    return ExecutionStatus::EXCEPTION;
1306
0
  }
1307
  // 2. Let S be ToString(O).
1308
  // 3. ReturnIfAbrupt(S).
1309
1
  auto strRes = toString_RJS(runtime, thisValue);
1310
1
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
1311
0
    return ExecutionStatus::EXCEPTION;
1312
0
  }
1313
1
  auto string = runtime.makeHandle(std::move(*strRes));
1314
1315
  // 4. Return CreateStringIterator(S).
1316
1
  return JSStringIterator::create(runtime, string).getHermesValue();
1317
1
}
1318
1319
CallResult<HermesValue>
1320
0
stringPrototypeMatchAll(void *, Runtime &runtime, NativeArgs args) {
1321
  // 1. Let O be ? RequireObjectCoercible(this value).
1322
0
  auto O = args.getThisHandle();
1323
0
  if (LLVM_UNLIKELY(
1324
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
1325
0
    return ExecutionStatus::EXCEPTION;
1326
0
  }
1327
1328
  // 2. If regexp is neither undefined nor null, then
1329
0
  auto regexp = args.getArgHandle(0);
1330
0
  if (!regexp->isUndefined() && !regexp->isNull()) {
1331
    // a. Let isRegExp be ? IsRegExp(regexp).
1332
0
    auto isRegExpRes = isRegExp(runtime, regexp);
1333
0
    if (LLVM_UNLIKELY(isRegExpRes == ExecutionStatus::EXCEPTION)) {
1334
0
      return ExecutionStatus::EXCEPTION;
1335
0
    }
1336
    // b. If isRegExp is true, then
1337
0
    if (*isRegExpRes) {
1338
      // Passing undefined and null checks imply regexp is an ObjectCoercible.
1339
0
      Handle<JSObject> regexpObj = Handle<JSObject>::vmcast(regexp);
1340
0
      bool isGlobal = false;
1341
      // i. Let flags be ? Get(regexp, "flags").
1342
0
      auto flagsPropRes = JSObject::getNamed_RJS(
1343
0
          regexpObj, runtime, Predefined::getSymbolID(Predefined::flags));
1344
0
      if (LLVM_UNLIKELY(flagsPropRes == ExecutionStatus::EXCEPTION)) {
1345
0
        return ExecutionStatus::EXCEPTION;
1346
0
      }
1347
0
      auto flags = runtime.makeHandle(std::move(*flagsPropRes));
1348
      // ii. Perform ? RequireObjectCoercible(flags).
1349
0
      if (LLVM_UNLIKELY(
1350
0
              checkObjectCoercible(runtime, flags) ==
1351
0
              ExecutionStatus::EXCEPTION)) {
1352
0
        return ExecutionStatus::EXCEPTION;
1353
0
      }
1354
      // iii. If ? ToString(flags) does not contain "g", throw a TypeError
1355
      // exception.
1356
0
      auto strRes = toString_RJS(runtime, flags);
1357
0
      if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
1358
0
        return ExecutionStatus::EXCEPTION;
1359
0
      }
1360
0
      auto strView = StringPrimitive::createStringView(
1361
0
          runtime, runtime.makeHandle(std::move(*strRes)));
1362
0
      for (char16_t c : strView)
1363
0
        if (c == u'g')
1364
0
          isGlobal = true;
1365
0
      if (!isGlobal)
1366
0
        return runtime.raiseTypeError(
1367
0
            "String.prototype.matchAll called with a non-global RegExp argument");
1368
0
    }
1369
    // c. Let matcher be ? GetMethod(regexp, @@matchAll).
1370
0
    auto matcherRes = getMethod(
1371
0
        runtime,
1372
0
        regexp,
1373
0
        runtime.makeHandle(
1374
0
            Predefined::getSymbolID(Predefined::SymbolMatchAll)));
1375
0
    if (LLVM_UNLIKELY(matcherRes == ExecutionStatus::EXCEPTION)) {
1376
0
      return ExecutionStatus::EXCEPTION;
1377
0
    }
1378
    // d. If matcher is not undefined, then
1379
0
    if (!matcherRes->getHermesValue().isUndefined()) {
1380
0
      auto matcher = runtime.makeHandle<Callable>(std::move(*matcherRes));
1381
      // i. Return ? Call(matcher, regexp, «O»).
1382
0
      return Callable::executeCall1(
1383
0
                 matcher, runtime, regexp, O.getHermesValue())
1384
0
          .toCallResultHermesValue();
1385
0
    }
1386
0
  }
1387
1388
  // 3. Let S be ? ToString(O).
1389
0
  auto strRes = toString_RJS(runtime, O);
1390
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
1391
0
    return ExecutionStatus::EXCEPTION;
1392
0
  }
1393
0
  auto S = runtime.makeHandle(std::move(*strRes));
1394
1395
  // 4. Let rx be ? RegExpCreate(regexp, "g").
1396
0
  auto regRes = regExpCreate(runtime, regexp, runtime.getCharacterString('g'));
1397
0
  if (regRes == ExecutionStatus::EXCEPTION) {
1398
0
    return ExecutionStatus::EXCEPTION;
1399
0
  }
1400
0
  Handle<JSRegExp> rx = regRes.getValue();
1401
1402
  // 5. Return ? Invoke(rx, @@matchAll, «S»).
1403
0
  auto propRes = JSObject::getNamed_RJS(
1404
0
      rx, runtime, Predefined::getSymbolID(Predefined::SymbolMatchAll));
1405
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1406
0
    return ExecutionStatus::EXCEPTION;
1407
0
  }
1408
0
  auto func =
1409
0
      Handle<Callable>::dyn_vmcast(runtime.makeHandle(std::move(*propRes)));
1410
0
  if (LLVM_UNLIKELY(!func)) {
1411
0
    return runtime.raiseTypeError(
1412
0
        "RegExp.prototype[@@matchAll] must be callable.");
1413
0
  }
1414
0
  return Callable::executeCall1(func, runtime, rx, S.getHermesValue())
1415
0
      .toCallResultHermesValue();
1416
0
}
1417
1418
/// This provides a shared implementation of three operations in ES2021:
1419
/// 6.1.4.1 Runtime Semantics: StringIndexOf ( string, searchValue, fromIndex )
1420
///   when clampPostion=false,
1421
/// 21.1.3.8 String.prototype.indexOf ( searchString [ , position ] )
1422
///   when reverse=false, and
1423
/// 21.1.3.9 String.prototype.lastIndexOf ( searchString [ , position ] )
1424
///   when reverse=true.
1425
///
1426
/// Given a haystack ('string'), needle ('searchString'), and position, return
1427
/// the index of the first (reverse=false) or last (reverse=true) substring
1428
/// match of needle within haystack that is not smaller (normal) or larger
1429
/// (reverse) than position.
1430
///
1431
/// \param runtime  the runtime to use for argument coercions
1432
/// \param string  represent the string searching from, i.e. "this" of
1433
///   indexOf / lastIndexOf or "string" of StringIndexOf.
1434
/// \param searchString  represent the substring searching for, i.e.
1435
///   "searchString" of indexOf / lastIndexOf or "searchValue" of StringIndexOf.
1436
/// \param position  represent the starting index of the search, i.e.
1437
///   "position" of indexOf / lastIndexOf or "fromIndex" of StringIndexOf.
1438
/// \param reverse  whether we are running lastIndexOf (true) or indexOf (false)
1439
/// \param clampPosition  whether the "position" is clamped to [0, length)
1440
///   (true if running indexOf / lastIndexOf) or not (running StringIndexOf).
1441
/// \returns Hermes-encoded index of the substring match, or -1 on failure
1442
static CallResult<HermesValue> stringDirectedIndexOf(
1443
    Runtime &runtime,
1444
    Handle<> string,
1445
    Handle<> searchString,
1446
    Handle<> position,
1447
    bool reverse,
1448
0
    bool clampPosition = true) {
1449
  // 1. Let O be ? RequireObjectCoercible(this value).
1450
  // Call a function that may throw, let the runtime record it.
1451
0
  if (LLVM_UNLIKELY(
1452
0
          checkObjectCoercible(runtime, string) ==
1453
0
          ExecutionStatus::EXCEPTION)) {
1454
0
    return ExecutionStatus::EXCEPTION;
1455
0
  }
1456
  // 2. Let S be ? ToString(O).
1457
0
  auto strRes = toString_RJS(runtime, string);
1458
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
1459
0
    return ExecutionStatus::EXCEPTION;
1460
0
  }
1461
0
  auto S = runtime.makeHandle(std::move(*strRes));
1462
1463
  // 3. Let searchStr be ? ToString(searchString).
1464
0
  auto searchStrRes = toString_RJS(runtime, searchString);
1465
0
  if (searchStrRes == ExecutionStatus::EXCEPTION) {
1466
0
    return ExecutionStatus::EXCEPTION;
1467
0
  }
1468
0
  auto searchStr = runtime.makeHandle(std::move(*searchStrRes));
1469
1470
0
  double pos;
1471
0
  if (reverse) {
1472
    // lastIndexOf
1473
    // 4. Let numPos be ? ToNumber(position).
1474
0
    auto intRes = toNumber_RJS(runtime, position);
1475
0
    if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1476
0
      return ExecutionStatus::EXCEPTION;
1477
0
    }
1478
0
    Handle<> numPos = runtime.makeHandle(intRes.getValue());
1479
    // 6. If numPos is NaN, let pos be +∞; otherwise, let pos be !
1480
    // ToIntegerOrInfinity(numPos).
1481
0
    if (std::isnan(numPos->getNumber())) {
1482
0
      pos = std::numeric_limits<double>::infinity();
1483
0
    } else {
1484
0
      if (LLVM_UNLIKELY(
1485
0
              (intRes = toIntegerOrInfinity(runtime, numPos)) ==
1486
0
              ExecutionStatus::EXCEPTION)) {
1487
0
        return ExecutionStatus::EXCEPTION;
1488
0
      }
1489
0
      pos = intRes->getNumber();
1490
0
    }
1491
0
  } else {
1492
    // indexOf
1493
    // 4. Let pos be ? ToIntegerOrInfinity(position).
1494
0
    auto intRes = toIntegerOrInfinity(runtime, position);
1495
0
    if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
1496
0
      return ExecutionStatus::EXCEPTION;
1497
0
    }
1498
0
    pos = intRes->getNumber();
1499
0
  }
1500
1501
  // Let len be the length of S.
1502
0
  double len = S->getStringLength();
1503
1504
  // When pos > len and "searchString" is an empty string, StringIndexOf behaves
1505
  // differently than String.prototype.indexOf in terms of the "clampPosition":
1506
  //   'aa'.indexOf('', 3) => 2               3 is clamped to 2.
1507
  //    StringIndexOf('aa', '', 3) => -1      3 is not clamped thus -1.
1508
  // Also, when pos > len and "searchString" is non-empty, they both fail.
1509
  // Therefore, it's safe to early return -1 for the case of StringIndexOf
1510
  // (i.e. clampPosition=false) as soon as pos > len is observed.
1511
0
  if (!clampPosition && pos > len) {
1512
0
    return HermesValue::encodeUntrustedNumberValue(-1);
1513
0
  }
1514
1515
  // Let start be min(max(pos, 0), len).
1516
0
  uint32_t start = static_cast<uint32_t>(std::min(std::max(pos, 0.), len));
1517
1518
  // TODO: good candidate for Boyer-Moore on large needles/haystacks
1519
  // TODO: good candidate for memchr on length-1 needles
1520
0
  auto SView = StringPrimitive::createStringView(runtime, S);
1521
0
  auto searchStrView = StringPrimitive::createStringView(runtime, searchStr);
1522
0
  double ret = -1;
1523
0
  if (reverse) {
1524
    // lastIndexOf
1525
0
    uint32_t lastPossibleMatchEnd =
1526
0
        std::min(SView.length(), start + searchStrView.length());
1527
0
    auto foundIter = std::search(
1528
0
        SView.rbegin() + (SView.length() - lastPossibleMatchEnd),
1529
0
        SView.rend(),
1530
0
        searchStrView.rbegin(),
1531
0
        searchStrView.rend());
1532
0
    if (foundIter != SView.rend() || searchStrView.empty()) {
1533
0
      ret = SView.rend() - foundIter - searchStrView.length();
1534
0
    }
1535
0
  } else {
1536
    // indexOf
1537
0
    auto foundIter = std::search(
1538
0
        SView.begin() + start,
1539
0
        SView.end(),
1540
0
        searchStrView.begin(),
1541
0
        searchStrView.end());
1542
0
    if (foundIter != SView.end() || searchStrView.empty()) {
1543
0
      ret = foundIter - SView.begin();
1544
0
    }
1545
0
  }
1546
0
  return HermesValue::encodeUntrustedNumberValue(ret);
1547
0
}
1548
1549
/// ES12 6.1.4.1 Runtime Semantics: StringIndexOf ( string, searchValue,
1550
/// fromIndex )
1551
/// This is currently implemented as a wrapper of stringDirectedIndexOf.
1552
/// Ideally, this can be implemented with less runtime checks and provide a fast
1553
/// path than stringDirectedIndexOf. TODO(T74338730)
1554
static CallResult<HermesValue> stringIndexOf(
1555
    Runtime &runtime,
1556
    Handle<StringPrimitive> string,
1557
    Handle<StringPrimitive> searchValue,
1558
0
    uint32_t fromIndex) {
1559
  // 1. Assert: Type(string) is String.
1560
  // 2. Assert: Type(searchValue) is String.
1561
  // 3. Assert: ! IsNonNegativeInteger(fromIndex) is true.
1562
0
  return stringDirectedIndexOf(
1563
0
      runtime,
1564
0
      string,
1565
0
      searchValue,
1566
0
      runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(fromIndex)),
1567
0
      false,
1568
0
      false);
1569
0
}
1570
1571
/// ES12 21.1.3.18 String.prototype.replaceAll ( searchValue, replaceValue )
1572
CallResult<HermesValue>
1573
0
stringPrototypeReplaceAll(void *, Runtime &runtime, NativeArgs args) {
1574
0
  GCScope gcScope{runtime};
1575
1576
  // 1. Let O be ? RequireObjectCoercible(this value).
1577
0
  auto O = args.getThisHandle();
1578
0
  if (LLVM_UNLIKELY(
1579
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
1580
0
    return ExecutionStatus::EXCEPTION;
1581
0
  }
1582
1583
0
  auto searchValue = args.getArgHandle(0);
1584
0
  auto replaceValue = args.getArgHandle(1);
1585
  // 2. If searchValue is neither undefined nor null, then
1586
0
  if (!searchValue->isUndefined() && !searchValue->isNull()) {
1587
    // a. Let isRegExp be ? IsRegExp(searchValue).
1588
0
    auto isRegExpRes = isRegExp(runtime, searchValue);
1589
0
    if (LLVM_UNLIKELY(isRegExpRes == ExecutionStatus::EXCEPTION)) {
1590
0
      return ExecutionStatus::EXCEPTION;
1591
0
    }
1592
    // b. If isRegExp is true, then
1593
0
    if (*isRegExpRes) {
1594
0
      Handle<JSObject> regexpObj = Handle<JSObject>::vmcast(searchValue);
1595
      // i. Let flags be ? Get(searchValue, "flags").
1596
0
      auto flagsPropRes = JSObject::getNamed_RJS(
1597
0
          regexpObj, runtime, Predefined::getSymbolID(Predefined::flags));
1598
0
      if (LLVM_UNLIKELY(flagsPropRes == ExecutionStatus::EXCEPTION)) {
1599
0
        return ExecutionStatus::EXCEPTION;
1600
0
      }
1601
0
      auto flags = runtime.makeHandle(std::move(*flagsPropRes));
1602
      // ii. Perform ? RequireObjectCoercible(flags).
1603
0
      if (LLVM_UNLIKELY(
1604
0
              checkObjectCoercible(runtime, flags) ==
1605
0
              ExecutionStatus::EXCEPTION)) {
1606
0
        return ExecutionStatus::EXCEPTION;
1607
0
      }
1608
      // iii. If ? ToString(flags) does not contain "g", throw a TypeError
1609
      // exception.
1610
0
      auto strRes = toString_RJS(runtime, flags);
1611
0
      if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
1612
0
        return ExecutionStatus::EXCEPTION;
1613
0
      }
1614
0
      auto strView = StringPrimitive::createStringView(
1615
0
          runtime, runtime.makeHandle(std::move(*strRes)));
1616
0
      bool isGlobal = false;
1617
0
      for (char16_t c : strView)
1618
0
        if (c == u'g')
1619
0
          isGlobal = true;
1620
0
      if (!isGlobal)
1621
0
        return runtime.raiseTypeError(
1622
0
            "String.prototype.replaceAll called with a non-global RegExp argument");
1623
0
    }
1624
    // c. Let replacer be ? GetMethod(searchValue, @@replace).
1625
0
    auto replacerRes = getMethod(
1626
0
        runtime,
1627
0
        searchValue,
1628
0
        runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolReplace)));
1629
0
    if (LLVM_UNLIKELY(replacerRes == ExecutionStatus::EXCEPTION)) {
1630
0
      return ExecutionStatus::EXCEPTION;
1631
0
    }
1632
    // d. If replacer is not undefined, then
1633
0
    if (!replacerRes->getHermesValue().isUndefined()) {
1634
0
      auto replacer = runtime.makeHandle<Callable>(std::move(*replacerRes));
1635
      // i. Return ? Call(replacer, searchValue, « O, replaceValue »)
1636
0
      return Callable::executeCall2(
1637
0
                 replacer,
1638
0
                 runtime,
1639
0
                 searchValue,
1640
0
                 O.getHermesValue(),
1641
0
                 replaceValue.getHermesValue())
1642
0
          .toCallResultHermesValue();
1643
0
    }
1644
0
  }
1645
1646
  // 3. Let string be ? ToString(O).
1647
0
  auto strRes = toString_RJS(runtime, O);
1648
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
1649
0
    return ExecutionStatus::EXCEPTION;
1650
0
  }
1651
0
  auto string = runtime.makeHandle(std::move(*strRes));
1652
1653
  // 4. Let searchString be ? ToString(searchValue).
1654
0
  auto searchStrRes = toString_RJS(runtime, searchValue);
1655
0
  if (LLVM_UNLIKELY(searchStrRes == ExecutionStatus::EXCEPTION)) {
1656
0
    return ExecutionStatus::EXCEPTION;
1657
0
  }
1658
0
  auto searchString = runtime.makeHandle(std::move(*searchStrRes));
1659
1660
  // 5. Let functionalReplace be IsCallable(replaceValue).
1661
0
  auto replaceFn = Handle<Callable>::dyn_vmcast(replaceValue);
1662
0
  bool functionalReplace = !!replaceFn;
1663
1664
  // It need to mutable since it's written here but read below.
1665
0
  MutableHandle<StringPrimitive> replaceValueStr{runtime};
1666
  // 6. If functionalReplace is false, then
1667
0
  if (!functionalReplace) {
1668
    // a. Set replaceValue to ? ToString(replaceValue).
1669
0
    auto replaceValueStrRes = toString_RJS(runtime, replaceValue);
1670
0
    if (LLVM_UNLIKELY(replaceValueStrRes == ExecutionStatus::EXCEPTION)) {
1671
0
      return ExecutionStatus::EXCEPTION;
1672
0
    }
1673
0
    replaceValueStr = std::move(*replaceValueStrRes);
1674
0
  }
1675
1676
  // 7. Let searchLength be the length of searchString.
1677
0
  uint32_t searchLength = searchString->getStringLength();
1678
  // 8. Let advanceBy be max(1, searchLength).
1679
0
  uint32_t advanceBy = std::max(1u, searchLength);
1680
1681
  // 9. Let matchPositions be a new empty List.
1682
0
  llvh::SmallVector<int32_t, 8> matchPositions{};
1683
1684
  // 10. Let position be ! StringIndexOf(string, searchString, 0).
1685
0
  auto positionRes = stringIndexOf(runtime, string, searchString, 0);
1686
0
  int32_t position = positionRes->getNumberAs<int32_t>();
1687
1688
  // 11. Repeat, while position is not -1,
1689
0
  while (position != -1) {
1690
0
    GCScopeMarkerRAII marker{runtime};
1691
    // a. Append position to the end of matchPositions.
1692
0
    matchPositions.push_back(position);
1693
    // b. Set position to ! StringIndexOf(string, searchString, position +
1694
    // advanceBy).
1695
0
    positionRes =
1696
0
        stringIndexOf(runtime, string, searchString, position + advanceBy);
1697
0
    assert(
1698
0
        positionRes == ExecutionStatus::RETURNED &&
1699
0
        "StringIndexOf cannot fail");
1700
0
    position = positionRes->getNumberAs<int32_t>();
1701
0
  }
1702
1703
  // 12. Let endOfLastMatch be 0.
1704
0
  uint32_t endOfLastMatch = 0;
1705
  // 13. Let result be the empty String value.
1706
0
  SmallU16String<32> result{};
1707
1708
  // 14. For each position in matchPositions, do
1709
0
  auto stringView = StringPrimitive::createStringView(runtime, string);
1710
0
  MutableHandle<StringPrimitive> replacement{runtime};
1711
0
  MutableHandle<> replacementCallRes{runtime};
1712
0
  for (uint32_t i = 0, size = matchPositions.size(); i < size; ++i) {
1713
0
    GCScopeMarkerRAII marker{runtime};
1714
0
    uint32_t position = matchPositions[i];
1715
    // a. Let preserved be the substring of string from endOfLastMatch to
1716
    // position.
1717
    // Noted that "substring" is from inclusiveStart to exclusiveEnd.
1718
0
    auto preserved =
1719
0
        stringView.slice(endOfLastMatch, position - endOfLastMatch);
1720
    // b. If functionalReplace is true, then
1721
0
    if (functionalReplace) {
1722
      // i. Let replacement be ? ToString(?
1723
      //   Call(replaceValue, undefined, « searchString, position, string »)).
1724
0
      auto callRes = Callable::executeCall3(
1725
0
                         replaceFn,
1726
0
                         runtime,
1727
0
                         Runtime::getUndefinedValue(),
1728
0
                         searchString.getHermesValue(),
1729
0
                         HermesValue::encodeUntrustedNumberValue(position),
1730
0
                         string.getHermesValue())
1731
0
                         .toCallResultHermesValue();
1732
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
1733
0
        return ExecutionStatus::EXCEPTION;
1734
0
      }
1735
0
      replacementCallRes = *callRes;
1736
0
      auto replacementStrRes = toString_RJS(runtime, replacementCallRes);
1737
0
      if (LLVM_UNLIKELY(replacementStrRes == ExecutionStatus::EXCEPTION)) {
1738
0
        return ExecutionStatus::EXCEPTION;
1739
0
      }
1740
0
      replacement = std::move(*replacementStrRes);
1741
0
    } else {
1742
      // c. Else,
1743
      // i. Assert: Type(replaceValue) is String.
1744
      // ii. Let captures be a new empty List.
1745
0
      auto captures = Runtime::makeNullHandle<ArrayStorageSmall>();
1746
0
      auto namedCaptures = Runtime::makeNullHandle<JSObject>();
1747
      // iii. Let replacement be ! GetSubstitution(searchString, string,
1748
      // position, captures, undefined, replaceValue).
1749
0
      auto callRes = getSubstitution(
1750
0
          runtime,
1751
0
          searchString,
1752
0
          string,
1753
0
          (double)position,
1754
0
          captures,
1755
0
          namedCaptures,
1756
0
          replaceValueStr);
1757
0
      if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
1758
0
        return ExecutionStatus::EXCEPTION;
1759
0
      }
1760
0
      replacement = vmcast<StringPrimitive>(*callRes);
1761
0
    }
1762
1763
    // d. Set result to the string-concatenation of result, preserved, and
1764
    // replacement.
1765
0
    preserved.appendUTF16String(result);
1766
0
    StringPrimitive::createStringView(runtime, replacement)
1767
0
        .appendUTF16String(result);
1768
    // e. Set endOfLastMatch to position + searchLength.
1769
0
    endOfLastMatch = position + searchLength;
1770
0
  }
1771
1772
  // 15. If endOfLastMatch < the length of string, then
1773
0
  if (endOfLastMatch < string->getStringLength()) {
1774
    // a. Set result to the string-concatenation of result and the substring of
1775
    // string from endOfLastMatch.
1776
0
    stringView.slice(endOfLastMatch).appendUTF16String(result);
1777
0
  }
1778
  // 16. Return result.
1779
0
  return StringPrimitive::create(runtime, result);
1780
0
}
1781
1782
CallResult<HermesValue>
1783
0
stringPrototypeMatch(void *, Runtime &runtime, NativeArgs args) {
1784
  // 1. Let O be RequireObjectCoercible(this value).
1785
  // 2. ReturnIfAbrupt(O).
1786
0
  auto O = args.getThisHandle();
1787
0
  if (LLVM_UNLIKELY(
1788
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
1789
0
    return ExecutionStatus::EXCEPTION;
1790
0
  }
1791
  // 3. If regexp is neither undefined nor null, then
1792
0
  auto regexp = args.getArgHandle(0);
1793
0
  if (!regexp->isUndefined() && !regexp->isNull()) {
1794
    // a. Let matcher be GetMethod(regexp, @@match).
1795
0
    auto methodRes = getMethod(
1796
0
        runtime,
1797
0
        regexp,
1798
0
        runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolMatch)));
1799
    // b. ReturnIfAbrupt(matcher).
1800
0
    if (LLVM_UNLIKELY(methodRes == ExecutionStatus::EXCEPTION)) {
1801
0
      return ExecutionStatus::EXCEPTION;
1802
0
    }
1803
    // c. If matcher is not undefined, then
1804
    //   i. Return Call(matcher, regexp, «‍O»).
1805
0
    if (!methodRes->getHermesValue().isUndefined()) {
1806
0
      auto matcher = runtime.makeHandle<Callable>(std::move(*methodRes));
1807
0
      return Callable::executeCall1(
1808
0
                 matcher, runtime, regexp, O.getHermesValue())
1809
0
          .toCallResultHermesValue();
1810
0
    }
1811
0
  }
1812
  // 4. Let S be ToString(O).
1813
0
  auto strRes = toString_RJS(runtime, O);
1814
  // 5. ReturnIfAbrupt(S).
1815
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
1816
0
    return ExecutionStatus::EXCEPTION;
1817
0
  }
1818
0
  auto S = runtime.makeHandle(std::move(*strRes));
1819
1820
  // 6. Let rx be RegExpCreate(regexp, undefined) (see 21.2.3.2.3).
1821
  // 7. ReturnIfAbrupt(rx).
1822
0
  auto regRes = regExpCreate(runtime, regexp, Runtime::getUndefinedValue());
1823
0
  if (regRes == ExecutionStatus::EXCEPTION) {
1824
0
    return ExecutionStatus::EXCEPTION;
1825
0
  }
1826
0
  Handle<JSRegExp> rx = regRes.getValue();
1827
1828
  // 8. Return Invoke(rx, @@match, «‍S»).
1829
0
  auto propRes = JSObject::getNamed_RJS(
1830
0
      rx, runtime, Predefined::getSymbolID(Predefined::SymbolMatch));
1831
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1832
0
    return ExecutionStatus::EXCEPTION;
1833
0
  }
1834
0
  auto func =
1835
0
      Handle<Callable>::dyn_vmcast(runtime.makeHandle(std::move(*propRes)));
1836
0
  if (LLVM_UNLIKELY(!func)) {
1837
0
    return runtime.raiseTypeError(
1838
0
        "RegExp.prototype[@@match] must be callable.");
1839
0
  }
1840
0
  return Callable::executeCall1(func, runtime, rx, S.getHermesValue())
1841
0
      .toCallResultHermesValue();
1842
0
}
1843
1844
CallResult<HermesValue>
1845
0
stringPrototypePad(void *ctx, Runtime &runtime, NativeArgs args) {
1846
0
  bool padStart = (bool)ctx;
1847
1848
  // 1. Let O be ? RequireObjectCoercible(this value).
1849
0
  auto O = args.getThisHandle();
1850
0
  if (LLVM_UNLIKELY(
1851
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
1852
0
    return ExecutionStatus::EXCEPTION;
1853
0
  }
1854
1855
  // 2. Let S be ? ToString(O).
1856
0
  auto sRes = toString_RJS(runtime, O);
1857
0
  if (LLVM_UNLIKELY(sRes == ExecutionStatus::EXCEPTION)) {
1858
0
    return ExecutionStatus::EXCEPTION;
1859
0
  }
1860
0
  auto S = runtime.makeHandle(std::move(*sRes));
1861
1862
  // 3. Let intMaxLength be ? ToLength(maxLength).
1863
0
  auto intMaxLengthRes = toLength(runtime, args.getArgHandle(0));
1864
0
  if (LLVM_UNLIKELY(intMaxLengthRes == ExecutionStatus::EXCEPTION)) {
1865
0
    return ExecutionStatus::EXCEPTION;
1866
0
  }
1867
0
  const uint64_t intMaxLength = intMaxLengthRes->getNumberAs<int64_t>();
1868
1869
  // 4. Let stringLength be the number of elements in S.
1870
0
  const uint32_t stringLength = S->getStringLength();
1871
0
  SafeUInt32 size{stringLength};
1872
1873
  // 5. If intMaxLength is not greater than stringLength, return S.
1874
0
  if (intMaxLength <= stringLength) {
1875
0
    return S.getHermesValue();
1876
0
  }
1877
1878
0
  MutableHandle<StringPrimitive> filler{runtime};
1879
1880
0
  if (args.getArg(1).isUndefined()) {
1881
    // 6. If fillString is undefined, let filler be a String consisting solely
1882
    // of the code unit 0x0020 (SPACE).
1883
0
    filler = runtime.getPredefinedString(Predefined::space);
1884
0
  } else {
1885
    // 7. Else, let filler be ? ToString(fillString).
1886
0
    auto fillerRes = toString_RJS(runtime, args.getArgHandle(1));
1887
0
    if (LLVM_UNLIKELY(fillerRes == ExecutionStatus::EXCEPTION)) {
1888
0
      return ExecutionStatus::EXCEPTION;
1889
0
    }
1890
0
    filler = fillerRes->get();
1891
0
  }
1892
1893
  // 8. If filler is the empty String, return S.
1894
0
  if (filler->getStringLength() == 0) {
1895
0
    return S.getHermesValue();
1896
0
  }
1897
1898
  // 9. Let fillLen be intMaxLength - stringLength.
1899
0
  const uint64_t fillLen = intMaxLength - stringLength;
1900
1901
  // Check for overflow and strings that are too long, so we can ensure uint32_t
1902
  // is a large enough type to use for math below.
1903
0
  if (fillLen > StringPrimitive::MAX_STRING_LENGTH) {
1904
0
    return runtime.raiseRangeError("String pad result exceeds limit");
1905
0
  }
1906
1907
0
  size.add((uint32_t)fillLen);
1908
1909
0
  if (size.isZero()) {
1910
0
    return HermesValue::encodeStringValue(
1911
0
        runtime.getPredefinedString(Predefined::emptyString));
1912
0
  }
1913
1914
0
  auto builderRes = StringBuilder::createStringBuilder(runtime, size);
1915
0
  if (LLVM_UNLIKELY(builderRes == ExecutionStatus::EXCEPTION)) {
1916
0
    return ExecutionStatus::EXCEPTION;
1917
0
  }
1918
1919
0
  uint32_t resultLen = size.get();
1920
1921
  // Repeatedly add filler to builder, taking up resultLen - stringLength
1922
  // characters.
1923
0
  auto addFiller = [&filler, resultLen, stringLength](StringBuilder &builder) {
1924
0
    uint32_t remaining = resultLen - stringLength;
1925
0
    const uint32_t fillerLen = filler->getStringLength();
1926
0
    while (remaining != 0) {
1927
0
      uint32_t length = std::min(remaining, fillerLen);
1928
0
      builder.appendStringPrim(filler, length);
1929
0
      remaining -= length;
1930
0
    }
1931
0
  };
1932
1933
0
  if (padStart) {
1934
    // 10. Let truncatedStringFiller be a new String value consisting of
1935
    // repeated concatenations of filler truncated to length fillLen.
1936
    // 11. Return a new String value computed by the concatenation of
1937
    // truncatedStringFiller and S.
1938
0
    addFiller(*builderRes);
1939
0
    builderRes->appendStringPrim(S);
1940
0
  } else {
1941
    // 10. Let truncatedStringFiller be a new String value consisting of
1942
    // repeated concatenations of filler truncated to length fillLen.
1943
    // 11. Return a new String value computed by the concatenation of S and
1944
    // truncatedStringFiller.
1945
0
    builderRes->appendStringPrim(S);
1946
0
    addFiller(*builderRes);
1947
0
  }
1948
1949
0
  return builderRes->getStringPrimitive().getHermesValue();
1950
0
}
1951
1952
CallResult<HermesValue>
1953
0
stringPrototypeReplace(void *, Runtime &runtime, NativeArgs args) {
1954
  // 1. Let O be RequireObjectCoercible(this value).
1955
  // 2. ReturnIfAbrupt(O).
1956
0
  auto O = args.getThisHandle();
1957
0
  if (LLVM_UNLIKELY(
1958
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
1959
0
    return ExecutionStatus::EXCEPTION;
1960
0
  }
1961
  // 3. If searchValue is neither undefined nor null, then
1962
0
  auto searchValue = args.getArgHandle(0);
1963
0
  auto replaceValue = args.getArgHandle(1);
1964
0
  if (!searchValue->isUndefined() && !searchValue->isNull()) {
1965
    // a. Let replacer be GetMethod(searchValue, @@replace).
1966
0
    auto methodRes = getMethod(
1967
0
        runtime,
1968
0
        searchValue,
1969
0
        runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolReplace)));
1970
    // b. ReturnIfAbrupt(replacer).
1971
0
    if (LLVM_UNLIKELY(methodRes == ExecutionStatus::EXCEPTION)) {
1972
0
      return ExecutionStatus::EXCEPTION;
1973
0
    }
1974
    // c. If replacer is not undefined, then
1975
    //   i. Return Call(replacer, searchValue, «‍O, replaceValue»).
1976
0
    if (!methodRes->getHermesValue().isUndefined()) {
1977
      // If methodRes is not Callable, step 3a would have thrown a TypeError.
1978
0
      Handle<Callable> replacer =
1979
0
          Handle<Callable>::vmcast(runtime, methodRes->getHermesValue());
1980
0
      return Callable::executeCall2(
1981
0
                 replacer,
1982
0
                 runtime,
1983
0
                 searchValue,
1984
0
                 O.getHermesValue(),
1985
0
                 replaceValue.getHermesValue())
1986
0
          .toCallResultHermesValue();
1987
0
    }
1988
0
  }
1989
  // 4. Let string be ToString(O).
1990
  // 5. ReturnIfAbrupt(string).
1991
0
  auto stringRes = toString_RJS(runtime, O);
1992
0
  if (LLVM_UNLIKELY(stringRes == ExecutionStatus::EXCEPTION)) {
1993
0
    return ExecutionStatus::EXCEPTION;
1994
0
  }
1995
0
  auto string = runtime.makeHandle(std::move(*stringRes));
1996
  // 6. Let searchString be ToString(searchValue).
1997
0
  auto searchStringRes = toString_RJS(runtime, searchValue);
1998
  // 7. ReturnIfAbrupt(searchString).
1999
0
  if (LLVM_UNLIKELY(searchStringRes == ExecutionStatus::EXCEPTION)) {
2000
0
    return ExecutionStatus::EXCEPTION;
2001
0
  }
2002
0
  auto searchString = runtime.makeHandle(std::move(*searchStringRes));
2003
  // 8. Let functionalReplace be IsCallable(replaceValue).
2004
0
  auto replaceFn = Handle<Callable>::dyn_vmcast(replaceValue);
2005
0
  MutableHandle<StringPrimitive> replaceValueStr{runtime};
2006
0
  bool functionalReplace = !!replaceFn;
2007
  // 9. If functionalReplace is false, then
2008
0
  if (!functionalReplace) {
2009
    // a. Let replaceValue be ToString(replaceValue).
2010
    // b. ReturnIfAbrupt(replaceValue).
2011
0
    auto replaceValueStrRes = toString_RJS(runtime, replaceValue);
2012
0
    if (LLVM_UNLIKELY(replaceValueStrRes == ExecutionStatus::EXCEPTION)) {
2013
0
      return ExecutionStatus::EXCEPTION;
2014
0
    }
2015
0
    replaceValueStr = std::move(*replaceValueStrRes);
2016
0
  }
2017
  // 10. Search string for the first occurrence of searchString and let pos be
2018
  // the index within string of the first code unit of the matched substring and
2019
  // let matched be searchString. If no occurrences of searchString were found,
2020
  // return string.
2021
  // Special case: if they're both empty then the match is at position 0.
2022
0
  uint32_t pos = 0;
2023
0
  auto strView = StringPrimitive::createStringView(runtime, string);
2024
0
  if (!strView.empty()) {
2025
0
    auto searchView = StringPrimitive::createStringView(runtime, searchString);
2026
0
    auto searchResult = std::search(
2027
0
        strView.begin(), strView.end(), searchView.begin(), searchView.end());
2028
2029
0
    if (searchResult != strView.end()) {
2030
0
      pos = searchResult - strView.begin();
2031
0
    } else {
2032
0
      return string.getHermesValue();
2033
0
    }
2034
0
  } else if (searchString->getStringLength() != 0) {
2035
    // If string is empty and search is not empty, there is no match.
2036
0
    return string.getHermesValue();
2037
0
  }
2038
0
  MutableHandle<StringPrimitive> replStr{runtime};
2039
  // 11. If functionalReplace is true, then
2040
0
  if (functionalReplace) {
2041
    // a. Let replValue be Call(replaceValue, undefined, «matched, pos, and
2042
    // string»).
2043
0
    auto callRes = Callable::executeCall3(
2044
0
        replaceFn,
2045
0
        runtime,
2046
0
        Runtime::getUndefinedValue(),
2047
0
        searchString.getHermesValue(),
2048
0
        HermesValue::encodeUntrustedNumberValue(pos),
2049
0
        string.getHermesValue());
2050
0
    if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
2051
0
      return ExecutionStatus::EXCEPTION;
2052
0
    }
2053
    // b. Let replStr be ToString(replValue).
2054
0
    auto replStrRes =
2055
0
        toString_RJS(runtime, runtime.makeHandle(std::move(*callRes)));
2056
    // c. ReturnIfAbrupt(replStr).
2057
0
    if (LLVM_UNLIKELY(replStrRes == ExecutionStatus::EXCEPTION)) {
2058
0
      return ExecutionStatus::EXCEPTION;
2059
0
    }
2060
0
    replStr = replStrRes->get();
2061
0
  } else {
2062
    // 12. Else,
2063
    // a. Let captures be an empty List.
2064
0
    auto nullHandle = Runtime::makeNullHandle<ArrayStorageSmall>();
2065
0
    auto namedCaptures = Runtime::makeNullHandle<JSObject>();
2066
    // b. Let replStr be GetSubstitution(matched, string, pos, captures,
2067
    // replaceValue).
2068
0
    auto callRes = getSubstitution(
2069
0
        runtime,
2070
0
        searchString,
2071
0
        string,
2072
0
        pos,
2073
0
        nullHandle,
2074
0
        namedCaptures,
2075
0
        replaceValueStr);
2076
0
    if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
2077
0
      return ExecutionStatus::EXCEPTION;
2078
0
    }
2079
0
    replStr = vmcast<StringPrimitive>(callRes.getValue());
2080
0
  }
2081
  // 13. Let tailPos be pos + the number of code units in matched.
2082
0
  uint32_t tailPos = pos + searchString->getStringLength();
2083
  // 14. Let newString be the String formed by concatenating the first pos code
2084
  // units of string, replStr, and the trailing substring of string starting at
2085
  // index tailPos. If pos is 0, the first element of the concatenation will be
2086
  // the empty String.
2087
0
  SmallU16String<32> newString{};
2088
0
  strView.slice(0, pos).appendUTF16String(newString);
2089
0
  StringPrimitive::createStringView(runtime, replStr)
2090
0
      .appendUTF16String(newString);
2091
0
  strView.slice(tailPos).appendUTF16String(newString);
2092
  // 15. Return newString.
2093
0
  return StringPrimitive::create(runtime, newString);
2094
0
}
2095
2096
CallResult<HermesValue>
2097
0
stringPrototypeSearch(void *, Runtime &runtime, NativeArgs args) {
2098
  // 1. Let O be RequireObjectCoercible(this value).
2099
  // 2. ReturnIfAbrupt(O).
2100
0
  auto O = args.getThisHandle();
2101
0
  if (LLVM_UNLIKELY(
2102
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
2103
0
    return ExecutionStatus::EXCEPTION;
2104
0
  }
2105
  // 3. If regexp is neither undefined nor null, then
2106
0
  auto regexp = args.getArgHandle(0);
2107
0
  if (!regexp->isUndefined() && !regexp->isNull()) {
2108
    // a. Let searcher be GetMethod(regexp, @@search).
2109
0
    auto methodRes = getMethod(
2110
0
        runtime,
2111
0
        regexp,
2112
0
        runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolSearch)));
2113
    // b. ReturnIfAbrupt(searcher).
2114
0
    if (LLVM_UNLIKELY(methodRes == ExecutionStatus::EXCEPTION)) {
2115
0
      return ExecutionStatus::EXCEPTION;
2116
0
    }
2117
    // c. If searcher is not undefined, then
2118
    //   i. Return Call(searcher, regexp, «‍O»).
2119
0
    if (!methodRes->getHermesValue().isUndefined()) {
2120
      // If methodRes is not Callable, step 3a would have thrown a TypeError.
2121
0
      Handle<Callable> searcher =
2122
0
          Handle<Callable>::vmcast(runtime, methodRes->getHermesValue());
2123
0
      return Callable::executeCall1(
2124
0
                 searcher, runtime, regexp, O.getHermesValue())
2125
0
          .toCallResultHermesValue();
2126
0
    }
2127
0
  }
2128
  // 4. Let string be ToString(O).
2129
  // 5. ReturnIfAbrupt(string).
2130
0
  auto strRes = toString_RJS(runtime, O);
2131
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
2132
0
    return ExecutionStatus::EXCEPTION;
2133
0
  }
2134
0
  auto string = runtime.makeHandle(std::move(*strRes));
2135
2136
  // 6. Let rx be RegExpCreate(regexp, undefined) (see 21.2.3.2.3).
2137
  // 7. ReturnIfAbrupt(rx).
2138
0
  auto regRes = regExpCreate(runtime, regexp, Runtime::getUndefinedValue());
2139
0
  if (regRes == ExecutionStatus::EXCEPTION) {
2140
0
    return ExecutionStatus::EXCEPTION;
2141
0
  }
2142
0
  Handle<JSRegExp> rx = *regRes;
2143
2144
  // 8. Return Invoke(rx, @@search, «string»).
2145
0
  auto propRes = JSObject::getNamed_RJS(
2146
0
      rx, runtime, Predefined::getSymbolID(Predefined::SymbolSearch));
2147
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2148
0
    return ExecutionStatus::EXCEPTION;
2149
0
  }
2150
0
  if (LLVM_UNLIKELY(!vmisa<Callable>(propRes->get()))) {
2151
0
    return runtime.raiseTypeError(
2152
0
        "RegExp.prototype[@@search] must be callable.");
2153
0
  }
2154
0
  auto func = Handle<Callable>::vmcast(runtime.makeHandle(std::move(*propRes)));
2155
0
  return Callable::executeCall1(func, runtime, rx, string.getHermesValue())
2156
0
      .toCallResultHermesValue();
2157
0
}
2158
2159
CallResult<HermesValue>
2160
0
stringPrototypeCharAt(void *, Runtime &runtime, NativeArgs args) {
2161
0
  Handle<> thisValue{&args.getThisArg()};
2162
  // Call a function that may throw, let the runtime record it.
2163
0
  if (LLVM_UNLIKELY(
2164
0
          checkObjectCoercible(runtime, thisValue) ==
2165
0
          ExecutionStatus::EXCEPTION)) {
2166
0
    return ExecutionStatus::EXCEPTION;
2167
0
  }
2168
0
  auto strRes = toString_RJS(runtime, thisValue);
2169
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
2170
0
    return ExecutionStatus::EXCEPTION;
2171
0
  }
2172
0
  auto S = runtime.makeHandle(std::move(*strRes));
2173
2174
0
  auto intRes =
2175
0
      toIntegerOrInfinity(runtime, runtime.makeHandle(args.getArg(0)));
2176
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2177
0
    return ExecutionStatus::EXCEPTION;
2178
0
  }
2179
0
  auto position = intRes->getNumber();
2180
0
  auto size = S->getStringLength();
2181
0
  if (position < 0 || position >= size) {
2182
0
    return HermesValue::encodeStringValue(
2183
0
        runtime.getPredefinedString(Predefined::emptyString));
2184
0
  }
2185
0
  auto result = runtime.getCharacterString(
2186
0
      StringPrimitive::createStringView(runtime, S)[position]);
2187
0
  return HermesValue::encodeStringValue(result.get());
2188
0
}
2189
2190
CallResult<HermesValue>
2191
0
stringPrototypeSlice(void *, Runtime &runtime, NativeArgs args) {
2192
0
  if (LLVM_UNLIKELY(
2193
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
2194
0
          ExecutionStatus::EXCEPTION)) {
2195
0
    return ExecutionStatus::EXCEPTION;
2196
0
  }
2197
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
2198
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
2199
0
    return ExecutionStatus::EXCEPTION;
2200
0
  }
2201
0
  auto S = runtime.makeHandle(std::move(*strRes));
2202
0
  double len = S->getStringLength();
2203
2204
0
  auto intRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
2205
0
  if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2206
0
    return ExecutionStatus::EXCEPTION;
2207
0
  }
2208
0
  double intStart = intRes->getNumber();
2209
2210
0
  double intEnd;
2211
0
  if (args.getArg(1).isUndefined()) {
2212
0
    intEnd = len;
2213
0
  } else {
2214
0
    if (LLVM_UNLIKELY(
2215
0
            (intRes = toIntegerOrInfinity(runtime, args.getArgHandle(1))) ==
2216
0
            ExecutionStatus::EXCEPTION)) {
2217
0
      return ExecutionStatus::EXCEPTION;
2218
0
    }
2219
0
    intEnd = intRes->getNumber();
2220
0
  }
2221
2222
0
  size_t from =
2223
0
      intStart < 0 ? std::max(len + intStart, 0.0) : std::min(intStart, len);
2224
0
  size_t to = intEnd < 0 ? std::max(len + intEnd, 0.0) : std::min(intEnd, len);
2225
0
  size_t span = to > from ? to - from : 0;
2226
0
  assert(from + span <= len && "invalid index computed in slice");
2227
2228
0
  return StringPrimitive::slice(runtime, S, from, span);
2229
0
}
2230
2231
CallResult<HermesValue>
2232
0
stringPrototypeEndsWith(void *, Runtime &runtime, NativeArgs args) {
2233
  // 1. Let O be RequireObjectCoercible(this value).
2234
0
  if (LLVM_UNLIKELY(
2235
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
2236
0
          ExecutionStatus::EXCEPTION)) {
2237
0
    return ExecutionStatus::EXCEPTION;
2238
0
  }
2239
2240
  // 2. Let S be ToString(O).
2241
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
2242
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
2243
0
    return ExecutionStatus::EXCEPTION;
2244
0
  }
2245
0
  auto S = runtime.makeHandle(std::move(*strRes));
2246
2247
  // 4. Let isRegExp be IsRegExp(searchString).
2248
0
  auto isRegExpRes = isRegExp(runtime, args.getArgHandle(0));
2249
0
  if (LLVM_UNLIKELY(isRegExpRes == ExecutionStatus::EXCEPTION)) {
2250
0
    return ExecutionStatus::EXCEPTION;
2251
0
  }
2252
2253
  // 6. If isRegExp is true, throw a TypeError exception.
2254
0
  if (LLVM_UNLIKELY(*isRegExpRes)) {
2255
0
    return runtime.raiseTypeError(
2256
0
        "First argument to endsWith must not be a RegExp");
2257
0
  }
2258
2259
  // 7. Let searchStr be ToString(searchString).
2260
0
  auto searchStrRes = toString_RJS(runtime, args.getArgHandle(0));
2261
0
  if (LLVM_UNLIKELY(searchStrRes == ExecutionStatus::EXCEPTION)) {
2262
0
    return ExecutionStatus::EXCEPTION;
2263
0
  }
2264
0
  auto searchStr = runtime.makeHandle(std::move(*searchStrRes));
2265
2266
  // 9. Let len be the number of elements in S.
2267
0
  double len = S->getStringLength();
2268
2269
  // 10. If endPosition is undefined, let pos be len, else let pos be
2270
  // ToIntegerOrInfinity(endPosition).
2271
0
  double pos;
2272
0
  if (args.getArg(1).isUndefined()) {
2273
0
    pos = len;
2274
0
  } else {
2275
0
    auto posRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
2276
0
    if (LLVM_UNLIKELY(posRes == ExecutionStatus::EXCEPTION)) {
2277
0
      return ExecutionStatus::EXCEPTION;
2278
0
    }
2279
0
    pos = posRes->getNumberAs<double>();
2280
0
  }
2281
2282
  // 12. Let end be min(max(pos, 0), len).
2283
0
  double end = std::min(std::max(pos, 0.0), len);
2284
2285
  // 13. Let searchLength be the number of elements in searchStr.
2286
0
  double searchLength = searchStr->getStringLength();
2287
2288
  // 14. Let start be end - searchLength.
2289
0
  double start = end - searchLength;
2290
2291
  // 15. If start is less than 0, return false.
2292
0
  if (start < 0) {
2293
0
    return HermesValue::encodeBoolValue(false);
2294
0
  }
2295
  // 16. If the sequence of elements of S starting at start of length
2296
  // searchLength is the same as the full element sequence of searchStr, return
2297
  // true.
2298
  // 17. Otherwise, return false.
2299
0
  return HermesValue::encodeBoolValue(
2300
0
      S->sliceEquals(start, searchLength, *searchStr));
2301
0
}
2302
2303
/// ES11 21.1.3.20.1
2304
/// Works slightly differently from the given implementation in the spec.
2305
/// Given a string \p S and a starting point \p q, finds the first match of
2306
/// \p R such that it starts on or after index \p q in \p S.
2307
/// \param q starting point in S. Requires: q <= S->getStringLength().
2308
/// \param R a String.
2309
/// \return Either None or an integer representing the start of the match.
2310
static OptValue<uint32_t> splitMatch(
2311
    Runtime &runtime,
2312
    Handle<StringPrimitive> S,
2313
    uint32_t q,
2314
0
    Handle<StringPrimitive> R) {
2315
  // 2. Let r be the number of code units in R.
2316
0
  auto r = R->getStringLength();
2317
  // 3. Let s be the number of code units in S.
2318
0
  auto s = S->getStringLength();
2319
  // 4. If q+r > s, return false.
2320
0
  if (q + r > s) {
2321
0
    return llvh::None;
2322
0
  }
2323
2324
  // Handle the case where the search starts at the end of the string and R is
2325
  // the empty string. This path should only be triggered when S is itself the
2326
  // empty string.
2327
0
  if (q == s) {
2328
0
    return q;
2329
0
  }
2330
2331
0
  auto SStr = StringPrimitive::createStringView(runtime, S);
2332
0
  auto RStr = StringPrimitive::createStringView(runtime, R);
2333
2334
0
  auto sliced = SStr.slice(q);
2335
0
  auto searchResult =
2336
0
      std::search(sliced.begin(), sliced.end(), RStr.begin(), RStr.end());
2337
2338
0
  if (searchResult != sliced.end()) {
2339
0
    return q + (searchResult - sliced.begin()) + r;
2340
0
  }
2341
0
  return llvh::None;
2342
0
}
2343
2344
// ES11 21.1.3.20 String.prototype.split ( separator, limit )
2345
CallResult<HermesValue>
2346
0
stringPrototypeSplit(void *, Runtime &runtime, NativeArgs args) {
2347
0
  GCScope gcScope{runtime};
2348
2349
  // 1. Let O be ? RequireObjectCoercible(this value).
2350
0
  auto O = args.getThisHandle();
2351
0
  if (LLVM_UNLIKELY(
2352
0
          checkObjectCoercible(runtime, O) == ExecutionStatus::EXCEPTION)) {
2353
0
    return ExecutionStatus::EXCEPTION;
2354
0
  }
2355
  // 2. If separator is neither undefined nor null, then
2356
0
  auto separator = args.getArgHandle(0);
2357
0
  if (!separator->isUndefined() && !separator->isNull()) {
2358
    // a. Let splitter be ? GetMethod(separator, @@split).
2359
0
    auto methodRes = getMethod(
2360
0
        runtime,
2361
0
        separator,
2362
0
        runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolSplit)));
2363
0
    if (LLVM_UNLIKELY(methodRes == ExecutionStatus::EXCEPTION)) {
2364
0
      return ExecutionStatus::EXCEPTION;
2365
0
    }
2366
    // b. If splitter is not undefined, then
2367
    // i. Return ? Call(splitter, separator, «‍O, limit»).
2368
0
    if (!methodRes->getHermesValue().isUndefined()) {
2369
0
      Handle<Callable> splitter =
2370
0
          Handle<Callable>::vmcast(runtime, methodRes->getHermesValue());
2371
0
      return Callable::executeCall2(
2372
0
                 splitter,
2373
0
                 runtime,
2374
0
                 separator,
2375
0
                 O.getHermesValue(),
2376
0
                 args.getArg(1))
2377
0
          .toCallResultHermesValue();
2378
0
    }
2379
0
  }
2380
2381
  // 3. Let S be ? ToString(O).
2382
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
2383
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
2384
0
    return ExecutionStatus::EXCEPTION;
2385
0
  }
2386
0
  auto S = runtime.makeHandle(std::move(*strRes));
2387
2388
  // 4. Let A be ! ArrayCreate(0).
2389
0
  auto arrRes = JSArray::create(runtime, 0, 0);
2390
0
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
2391
0
    return ExecutionStatus::EXCEPTION;
2392
0
  }
2393
0
  auto A = *arrRes;
2394
  // 5. Let lengthA be 0.
2395
0
  uint32_t lengthA = 0;
2396
2397
  // 6. If limit is undefined, let lim be 232 - 1; else let lim be ?
2398
  // ToUint32(limit).
2399
0
  auto limit = args.getArgHandle(1);
2400
0
  uint32_t lim;
2401
0
  if (limit->isUndefined()) {
2402
    // No limit supplied, make it max.
2403
0
    lim = 0xffffffff; // 2 ^ 32 - 1
2404
0
  } else {
2405
0
    auto intRes = toUInt32_RJS(runtime, limit);
2406
0
    if (LLVM_UNLIKELY(intRes == ExecutionStatus::EXCEPTION)) {
2407
0
      return ExecutionStatus::EXCEPTION;
2408
0
    }
2409
0
    lim = intRes->getNumber();
2410
0
  }
2411
2412
  // 7. Let s be the length of S.
2413
0
  uint32_t s = S->getStringLength();
2414
  // 8. Let p be 0.
2415
  // End of the last match.
2416
0
  uint32_t p = 0;
2417
2418
  // 9. Let R be ? ToString(separator).
2419
  // The pattern which we want to separate on.
2420
0
  auto sepRes = toString_RJS(runtime, args.getArgHandle(0));
2421
0
  if (LLVM_UNLIKELY(sepRes == ExecutionStatus::EXCEPTION)) {
2422
0
    return ExecutionStatus::EXCEPTION;
2423
0
  }
2424
0
  Handle<StringPrimitive> R = runtime.makeHandle(std::move(sepRes.getValue()));
2425
2426
  // 10. If lim = 0, return A.
2427
0
  if (lim == 0) {
2428
    // Don't want any elements, so we're done.
2429
0
    return A.getHermesValue();
2430
0
  }
2431
2432
  // 11. If separator is undefined, then
2433
0
  if (LLVM_UNLIKELY(separator->isUndefined())) {
2434
    // a. Perform ! CreateDataPropertyOrThrow(A, "0", S).
2435
0
    (void)JSArray::setElementAt(A, runtime, 0, S);
2436
0
    if (LLVM_UNLIKELY(
2437
0
            JSArray::setLengthProperty(A, runtime, 1) ==
2438
0
            ExecutionStatus::EXCEPTION))
2439
0
      return ExecutionStatus::EXCEPTION;
2440
    // b. Return A.
2441
0
    return A.getHermesValue();
2442
0
  }
2443
2444
  // 12. If s = 0, then
2445
0
  if (s == 0) {
2446
    // S is the empty string.
2447
    // a. Let z be SplitMatch(S, 0, R).
2448
0
    auto matchResult = splitMatch(runtime, S, 0, R);
2449
    // b. If z is not false, return A.
2450
0
    if (matchResult) {
2451
0
      return A.getHermesValue();
2452
0
    }
2453
    // c. Perform ! CreateDataPropertyOrThrow(A, "0", S).
2454
0
    (void)JSArray::setElementAt(A, runtime, 0, S);
2455
0
    if (LLVM_UNLIKELY(
2456
0
            JSArray::setLengthProperty(A, runtime, 1) ==
2457
0
            ExecutionStatus::EXCEPTION))
2458
0
      return ExecutionStatus::EXCEPTION;
2459
    // e. Return A.
2460
0
    return A.getHermesValue();
2461
0
  }
2462
2463
  // 17. Let q be p.
2464
  // Place to attempt the start of the next match.
2465
0
  uint32_t q = p;
2466
2467
0
  MutableHandle<> tmpHandle{runtime};
2468
0
  auto marker = gcScope.createMarker();
2469
2470
  // 18. Repeat, while q ≠ s
2471
  // Main loop: continue while we have space to find another match.
2472
0
  while (q != s) {
2473
0
    gcScope.flushToMarker(marker);
2474
2475
    // a. Let e be SplitMatch(S, q, R).
2476
    // b. If e is false, let q = q+1.
2477
2478
    // Find the next valid match. We know that q < s.
2479
    // ES6's SplitMatch only finds matches at q, but we find matches at or
2480
    // after q, so if it fails, we know we're done. In effect, we are performing
2481
    // steps (a) and (b) over and over again.
2482
0
    auto matchResult = splitMatch(runtime, S, q, R);
2483
    // If we did find a match, fast-forward q to the start of that match.
2484
0
    if (matchResult) {
2485
0
      q = *matchResult - R->getStringLength();
2486
0
    }
2487
0
    if (!matchResult || q >= s) {
2488
      // There's no matches between index q and the end of the string, so we're
2489
      // done searching. Note: This behavior differs from the spec
2490
      // implementation, because we check for matches at or after q. However, in
2491
      // line with the spec, we only count matches that start before the end of
2492
      // the string.
2493
0
      break;
2494
0
    }
2495
    // c. Else,
2496
    // Found a match, so go ahead and update e, such that the match is the range
2497
    // [q,e).
2498
0
    uint32_t e = *matchResult;
2499
    // i. If e = p, set q to q + 1.
2500
0
    if (e == p) {
2501
      // The end of this match is the same as the end of the last match,
2502
      // so we matched with the empty string.
2503
      // We don't want to match the empty string at this location again,
2504
      // so increment q in order to start the next search at the next position.
2505
0
      q++;
2506
0
    }
2507
    // ii. Else,
2508
0
    else {
2509
      // Found a non-empty string match.
2510
      // Add everything from the last match to the current one to A.
2511
      // This has length q-p because q is the start of the current match,
2512
      // and p was the end (exclusive) of the last match.
2513
2514
      // 1. Let T be the String value equal to the substring of S consisting of
2515
      // the code units at indices p (inclusive) through q (exclusive).
2516
0
      auto strRes = StringPrimitive::slice(runtime, S, p, q - p);
2517
0
      if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
2518
0
        return ExecutionStatus::EXCEPTION;
2519
0
      }
2520
0
      tmpHandle = *strRes;
2521
      // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(lengthA), T)
2522
0
      JSArray::setElementAt(A, runtime, lengthA, tmpHandle);
2523
      // 3. Set lengthA to lengthA + 1.
2524
0
      ++lengthA;
2525
2526
      // 4. If lengthA = lim, return A.
2527
0
      if (lengthA == lim) {
2528
        // Reached the limit, return early.
2529
0
        if (LLVM_UNLIKELY(
2530
0
                JSArray::setLengthProperty(A, runtime, lengthA) ==
2531
0
                ExecutionStatus::EXCEPTION))
2532
0
          return ExecutionStatus::EXCEPTION;
2533
0
        return A.getHermesValue();
2534
0
      }
2535
      // 5. Set p to e.
2536
      // Update p to point to the end of this match, maintaining the
2537
      // invariant that it points to the end of the last match encountered.
2538
0
      p = e;
2539
      // 6. Set q to p.
2540
      // Start position of the next search is updated to the end of this match.
2541
0
      q = p;
2542
0
    }
2543
0
  }
2544
2545
  // 15. Let T be the String value equal to the substring of S consisting of the
2546
  // code units at indices p (inclusive) through s (exclusive).
2547
  // Add the rest of the string (after the last match) to A.
2548
0
  auto elementStrRes = StringPrimitive::slice(runtime, S, p, s - p);
2549
0
  if (LLVM_UNLIKELY(elementStrRes == ExecutionStatus::EXCEPTION)) {
2550
0
    return ExecutionStatus::EXCEPTION;
2551
0
  }
2552
0
  tmpHandle = *elementStrRes;
2553
  // 16. Perform ! CreateDataPropertyOrThrow(A, ! ToString(lengthA), T).
2554
0
  JSArray::setElementAt(A, runtime, lengthA, tmpHandle);
2555
0
  ++lengthA;
2556
2557
0
  if (LLVM_UNLIKELY(
2558
0
          JSArray::setLengthProperty(A, runtime, lengthA) ==
2559
0
          ExecutionStatus::EXCEPTION))
2560
0
    return ExecutionStatus::EXCEPTION;
2561
2562
  // 27. Return A.
2563
0
  return A.getHermesValue();
2564
0
}
2565
2566
CallResult<HermesValue> stringPrototypeIncludesOrStartsWith(
2567
    void *ctx,
2568
    Runtime &runtime,
2569
0
    NativeArgs args) {
2570
  // If true, perform the startsWith operation.
2571
  // Else, do the standard "includes" operation.
2572
0
  bool startsWith = static_cast<bool>(ctx);
2573
2574
  // 1. Let O be RequireObjectCoercible(this value).
2575
0
  if (LLVM_UNLIKELY(
2576
0
          checkObjectCoercible(runtime, args.getThisHandle()) ==
2577
0
          ExecutionStatus::EXCEPTION)) {
2578
0
    return ExecutionStatus::EXCEPTION;
2579
0
  }
2580
2581
  // 2. Let S be ToString(O).
2582
0
  auto strRes = toString_RJS(runtime, args.getThisHandle());
2583
0
  if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
2584
0
    return ExecutionStatus::EXCEPTION;
2585
0
  }
2586
0
  auto S = runtime.makeHandle(std::move(*strRes));
2587
2588
  // 4. Let isRegExp be IsRegExp(searchString).
2589
  // 6. If isRegExp is true, throw a TypeError exception.
2590
0
  auto isRegExpRes = isRegExp(runtime, args.getArgHandle(0));
2591
0
  if (LLVM_UNLIKELY(isRegExpRes == ExecutionStatus::EXCEPTION)) {
2592
0
    return ExecutionStatus::EXCEPTION;
2593
0
  }
2594
2595
0
  if (LLVM_UNLIKELY(*isRegExpRes)) {
2596
0
    return runtime.raiseTypeError(
2597
0
        "First argument to startsWith and includes must not be a RegExp");
2598
0
  }
2599
2600
  // 7. Let searchStr be ToString(searchString).
2601
0
  auto searchStrRes = toString_RJS(runtime, args.getArgHandle(0));
2602
0
  if (LLVM_UNLIKELY(searchStrRes == ExecutionStatus::EXCEPTION)) {
2603
0
    return ExecutionStatus::EXCEPTION;
2604
0
  }
2605
0
  auto searchStr = runtime.makeHandle(std::move(*searchStrRes));
2606
2607
  // 9. Let pos be ToIntegerOrInfinity(position).
2608
  // (If position is undefined, this step produces the value 0).
2609
0
  auto posRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
2610
0
  if (LLVM_UNLIKELY(posRes == ExecutionStatus::EXCEPTION)) {
2611
0
    return ExecutionStatus::EXCEPTION;
2612
0
  }
2613
0
  double pos = posRes->getNumber();
2614
2615
  // 11. Let len be the number of elements in S.
2616
0
  double len = S->getStringLength();
2617
2618
  // 12. Let start be min(max(pos, 0), len).
2619
0
  double start = std::min(std::max(pos, 0.0), len);
2620
2621
  // 13. Let searchLength be the number of elements in searchStr.
2622
0
  double searchLength = searchStr->getStringLength();
2623
2624
0
  if (startsWith) {
2625
    // Perform the startsWith operation instead of includes.
2626
    // 14. If searchLength+start is greater than len, return false.
2627
0
    if (searchLength + start > len) {
2628
0
      return HermesValue::encodeBoolValue(false);
2629
0
    }
2630
    // 15. If the sequence of elements of S starting at start of length
2631
    // searchLength is the same as the full element sequence of searchStr,
2632
    // return true.
2633
    // 16. Otherwise, return false.
2634
0
    return HermesValue::encodeBoolValue(
2635
0
        S->sliceEquals(start, searchLength, *searchStr));
2636
0
  }
2637
2638
  // 14. If there exists any integer k not smaller than start such that k +
2639
  // searchLen is not greater than len, and for all nonnegative integers j less
2640
  // than searchLen, the code unit at index k+j of S is the same as the code
2641
  // unit at index j of searchStr, return true; but if there is no such integer
2642
  // k, return false.
2643
0
  auto SView = StringPrimitive::createStringView(runtime, S);
2644
0
  auto searchStrView = StringPrimitive::createStringView(runtime, searchStr);
2645
0
  auto foundIter = std::search(
2646
0
      SView.begin() + start,
2647
0
      SView.end(),
2648
0
      searchStrView.begin(),
2649
0
      searchStrView.end());
2650
  // Note: searchStrView.empty check is needed in the special case that S is
2651
  // empty, searchStr is empty, and start = 0
2652
0
  return HermesValue::encodeBoolValue(
2653
0
      foundIter != SView.end() || searchStrView.empty());
2654
0
}
2655
2656
CallResult<HermesValue>
2657
0
stringPrototypeIndexOf(void *, Runtime &runtime, NativeArgs args) {
2658
0
  auto searchString = args.getArgHandle(0);
2659
0
  auto position = args.getArgHandle(1);
2660
0
  return stringDirectedIndexOf(
2661
0
      runtime, args.getThisHandle(), searchString, position, false);
2662
0
}
2663
2664
CallResult<HermesValue>
2665
0
stringPrototypeLastIndexOf(void *, Runtime &runtime, NativeArgs args) {
2666
0
  auto searchString = args.getArgHandle(0);
2667
0
  auto position = args.getArgHandle(1);
2668
0
  return stringDirectedIndexOf(
2669
0
      runtime, args.getThisHandle(), searchString, position, true);
2670
0
}
2671
2672
} // namespace vm
2673
} // namespace hermes