Coverage Report

Created: 2023-11-19 07:23

/src/hermes/lib/VM/Operations.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#include "hermes/VM/Operations.h"
9
10
#include "hermes/Support/Conversions.h"
11
#include "hermes/Support/OSCompat.h"
12
#include "hermes/VM/BigIntPrimitive.h"
13
#include "hermes/VM/Callable.h"
14
#include "hermes/VM/Casting.h"
15
#include "hermes/VM/JSArray.h"
16
#include "hermes/VM/JSCallableProxy.h"
17
#include "hermes/VM/JSError.h"
18
#include "hermes/VM/JSObject.h"
19
#include "hermes/VM/JSRegExp.h"
20
#include "hermes/VM/PrimitiveBox.h"
21
#include "hermes/VM/PropertyAccessor.h"
22
#include "hermes/VM/Runtime.h"
23
#include "hermes/VM/StringBuilder.h"
24
#include "hermes/VM/StringPrimitive.h"
25
#include "hermes/VM/StringView.h"
26
27
#include "dtoa/dtoa.h"
28
29
#include "llvh/ADT/SmallString.h"
30
31
#include <cfloat>
32
#include <cmath>
33
34
namespace hermes {
35
namespace vm {
36
37
CallResult<Handle<SymbolID>> stringToSymbolID(
38
    Runtime &runtime,
39
183k
    PseudoHandle<StringPrimitive> strPrim) {
40
  // Unique the string.
41
183k
  return runtime.getIdentifierTable().getSymbolHandleFromPrimitive(
42
183k
      runtime, std::move(strPrim));
43
183k
}
44
45
CallResult<Handle<SymbolID>> valueToSymbolID(
46
    Runtime &runtime,
47
184k
    Handle<> nameValHnd) {
48
184k
  if (nameValHnd->isSymbol()) {
49
495
    return Handle<SymbolID>::vmcast(nameValHnd);
50
495
  }
51
  // Convert the value to a string.
52
183k
  auto res = toString_RJS(runtime, nameValHnd);
53
183k
  if (res == ExecutionStatus::EXCEPTION)
54
0
    return ExecutionStatus::EXCEPTION;
55
56
  // Unique the string.
57
183k
  return stringToSymbolID(runtime, std::move(*res));
58
183k
}
59
60
1.26k
HermesValue typeOf(Runtime &runtime, Handle<> valueHandle) {
61
1.26k
  switch (valueHandle->getETag()) {
62
0
    case HermesValue::ETag::Undefined:
63
0
      return HermesValue::encodeStringValue(
64
0
          runtime.getPredefinedString(Predefined::undefined));
65
0
    case HermesValue::ETag::Null:
66
0
      return HermesValue::encodeStringValue(
67
0
          runtime.getPredefinedString(Predefined::object));
68
0
    case HermesValue::ETag::Str1:
69
0
    case HermesValue::ETag::Str2:
70
0
      return HermesValue::encodeStringValue(
71
0
          runtime.getPredefinedString(Predefined::string));
72
0
    case HermesValue::ETag::BigInt1:
73
0
    case HermesValue::ETag::BigInt2:
74
0
      return HermesValue::encodeStringValue(
75
0
          runtime.getPredefinedString(Predefined::bigint));
76
0
    case HermesValue::ETag::Bool:
77
0
      return HermesValue::encodeStringValue(
78
0
          runtime.getPredefinedString(Predefined::boolean));
79
0
    case HermesValue::ETag::Symbol:
80
0
      return HermesValue::encodeStringValue(
81
0
          runtime.getPredefinedString(Predefined::symbol));
82
1.26k
    case HermesValue::ETag::Object1:
83
1.26k
    case HermesValue::ETag::Object2:
84
1.26k
      if (vmisa<Callable>(*valueHandle))
85
630
        return HermesValue::encodeStringValue(
86
630
            runtime.getPredefinedString(Predefined::function));
87
630
      return HermesValue::encodeStringValue(
88
630
          runtime.getPredefinedString(Predefined::object));
89
0
    default:
90
0
      assert(valueHandle->isNumber() && "Invalid type.");
91
0
      return HermesValue::encodeStringValue(
92
0
          runtime.getPredefinedString(Predefined::number));
93
1.26k
  }
94
1.26k
}
95
96
OptValue<uint32_t> toArrayIndex(
97
    Runtime &runtime,
98
0
    Handle<StringPrimitive> strPrim) {
99
0
  auto view = StringPrimitive::createStringView(runtime, strPrim);
100
0
  return toArrayIndex(view);
101
0
}
102
103
231k
OptValue<uint32_t> toArrayIndex(StringView str) {
104
231k
  auto len = str.length();
105
231k
  if (str.isASCII()) {
106
231k
    const char *ptr = str.castToCharPtr();
107
231k
    return hermes::toArrayIndex(ptr, ptr + len);
108
231k
  }
109
0
  const char16_t *ptr = str.castToChar16Ptr();
110
0
  return hermes::toArrayIndex(ptr, ptr + len);
111
231k
}
112
113
535
bool isSameValue(HermesValue x, HermesValue y) {
114
535
  if (x.getTag() != y.getTag()) {
115
    // If the tags are different, they must be different.
116
0
    return false;
117
0
  }
118
535
  assert(
119
535
      !x.isEmpty() && !x.isNativeValue() &&
120
535
      "Empty and Native Value cannot be compared");
121
122
  // Strings require deep comparison.
123
535
  if (x.isString()) {
124
    // For strings, we compare each character in sequence.
125
10
    return x.getString()->equals(y.getString());
126
10
  }
127
128
  // Bigints also require deep comparison.
129
525
  if (x.isBigInt()) {
130
    // For bigints, perform the numerical comparison.
131
0
    return x.getBigInt()->compare(y.getBigInt()) == 0;
132
0
  }
133
134
  // Otherwise they are identical if the raw bits are the same.
135
525
  return x.getRaw() == y.getRaw();
136
525
}
137
138
0
bool isSameValueZero(HermesValue x, HermesValue y) {
139
0
  if (x.isNumber() && y.isNumber() && x.getNumber() == y.getNumber()) {
140
    // Takes care of +0 == -0.
141
0
    return true;
142
0
  }
143
0
  return isSameValue(x, y);
144
0
}
145
146
651
bool isPrimitive(HermesValue val) {
147
651
  assert(!val.isEmpty() && "empty value encountered");
148
0
  assert(!val.isNativeValue() && "native value encountered");
149
0
  return !val.isObject();
150
651
}
151
152
CallResult<HermesValue> ordinaryToPrimitive(
153
    Handle<JSObject> selfHandle,
154
    Runtime &runtime,
155
412
    PreferredType preferredType) {
156
412
  GCScope gcScope{runtime};
157
412
  assert(
158
412
      preferredType != PreferredType::NONE &&
159
412
      "OrdinaryToPrimitive requires a type hint");
160
161
651
  for (int i = 0; i < 2; ++i) {
162
651
    if (preferredType == PreferredType::STRING) {
163
412
      auto propRes = JSObject::getNamed_RJS(
164
412
          selfHandle, runtime, Predefined::getSymbolID(Predefined::toString));
165
412
      if (propRes == ExecutionStatus::EXCEPTION)
166
0
        return ExecutionStatus::EXCEPTION;
167
412
      if (auto funcHandle = Handle<Callable>::dyn_vmcast(
168
412
              runtime.makeHandle(std::move(*propRes)))) {
169
412
        auto callRes =
170
412
            funcHandle->executeCall0(funcHandle, runtime, selfHandle);
171
412
        if (callRes == ExecutionStatus::EXCEPTION)
172
0
          return ExecutionStatus::EXCEPTION;
173
412
        if (isPrimitive(callRes->get()))
174
412
          return callRes.toCallResultHermesValue();
175
412
      }
176
177
      // This method failed. Try the other one.
178
0
      preferredType = PreferredType::NUMBER;
179
239
    } else {
180
239
      auto propRes = JSObject::getNamed_RJS(
181
239
          selfHandle, runtime, Predefined::getSymbolID(Predefined::valueOf));
182
239
      if (propRes == ExecutionStatus::EXCEPTION)
183
0
        return ExecutionStatus::EXCEPTION;
184
239
      if (auto funcHandle = Handle<Callable>::dyn_vmcast(
185
239
              runtime.makeHandle(std::move(*propRes)))) {
186
239
        auto callRes =
187
239
            funcHandle->executeCall0(funcHandle, runtime, selfHandle);
188
239
        if (callRes == ExecutionStatus::EXCEPTION)
189
0
          return ExecutionStatus::EXCEPTION;
190
239
        if (isPrimitive(callRes->get()))
191
0
          return callRes.toCallResultHermesValue();
192
239
      }
193
194
      // This method failed. Try the other one.
195
239
      preferredType = PreferredType::STRING;
196
239
    }
197
651
  }
198
199
  // Nothing succeeded, time to give up.
200
0
  return runtime.raiseTypeError("Cannot determine default value of object");
201
412
}
202
203
/// ES5.1 9.1
204
CallResult<HermesValue>
205
62.6k
toPrimitive_RJS(Runtime &runtime, Handle<> valueHandle, PreferredType hint) {
206
62.6k
  assert(!valueHandle->isEmpty() && "empty value is not allowed");
207
0
  assert(!valueHandle->isNativeValue() && "native value is not allowed");
208
209
62.6k
  if (!valueHandle->isObject())
210
62.1k
    return *valueHandle;
211
212
  // 4. Let exoticToPrim be GetMethod(input, @@toPrimitive).
213
412
  auto exoticToPrim = getMethod(
214
412
      runtime,
215
412
      valueHandle,
216
412
      runtime.makeHandle(
217
412
          Predefined::getSymbolID(Predefined::SymbolToPrimitive)));
218
412
  if (LLVM_UNLIKELY(exoticToPrim == ExecutionStatus::EXCEPTION)) {
219
0
    return ExecutionStatus::EXCEPTION;
220
0
  }
221
  // 6. If exoticToPrim is not undefined, then
222
412
  if (vmisa<Callable>(exoticToPrim->getHermesValue())) {
223
0
    auto callable = runtime.makeHandle<Callable>(
224
0
        dyn_vmcast<Callable>(exoticToPrim->getHermesValue()));
225
0
    CallResult<PseudoHandle<>> resultRes = Callable::executeCall1(
226
0
        callable,
227
0
        runtime,
228
0
        valueHandle,
229
0
        HermesValue::encodeStringValue(runtime.getPredefinedString(
230
0
            hint == PreferredType::NONE         ? Predefined::defaultStr
231
0
                : hint == PreferredType::STRING ? Predefined::string
232
0
                                                : Predefined::number)));
233
0
    if (LLVM_UNLIKELY(resultRes == ExecutionStatus::EXCEPTION)) {
234
0
      return ExecutionStatus::EXCEPTION;
235
0
    }
236
0
    PseudoHandle<> result = std::move(*resultRes);
237
0
    if (!result->isObject()) {
238
0
      return result.getHermesValue();
239
0
    }
240
0
    return runtime.raiseTypeError(
241
0
        "Symbol.toPrimitive function must return a primitive");
242
0
  }
243
244
  // 7. If hint is "default", let hint be "number".
245
  // 8. Return OrdinaryToPrimitive(input,hint).
246
412
  return ordinaryToPrimitive(
247
412
      Handle<JSObject>::vmcast(valueHandle),
248
412
      runtime,
249
412
      hint == PreferredType::NONE ? PreferredType::NUMBER : hint);
250
412
}
251
252
1.16k
bool toBoolean(HermesValue value) {
253
1.16k
  switch (value.getETag()) {
254
0
#ifdef HERMES_SLOW_DEBUG
255
0
    case HermesValue::ETag::Invalid:
256
0
      llvm_unreachable("invalid value");
257
0
#endif // HERMES_SLOW_DEBUG
258
0
    case HermesValue::ETag::Empty:
259
0
      llvm_unreachable("empty value");
260
0
    case HermesValue::ETag::Native1:
261
0
    case HermesValue::ETag::Native2:
262
0
      llvm_unreachable("native value");
263
0
    case HermesValue::ETag::Undefined:
264
0
    case HermesValue::ETag::Null:
265
0
      return false;
266
1.16k
    case HermesValue::ETag::Bool:
267
1.16k
      return value.getBool();
268
0
    case HermesValue::ETag::Symbol:
269
0
    case HermesValue::ETag::Object1:
270
0
    case HermesValue::ETag::Object2:
271
0
      return true;
272
0
    case HermesValue::ETag::BigInt1:
273
0
    case HermesValue::ETag::BigInt2:
274
0
      return value.getBigInt()->compare(0) != 0;
275
0
    case HermesValue::ETag::Str1:
276
0
    case HermesValue::ETag::Str2:
277
0
      return value.getString()->getStringLength() != 0;
278
0
    default: {
279
0
      auto m = value.getNumber();
280
0
      return !(m == 0 || std::isnan(m));
281
0
    }
282
1.16k
  }
283
1.16k
}
284
285
/// ES5.1 9.8.1
286
static CallResult<PseudoHandle<StringPrimitive>> numberToString(
287
    Runtime &runtime,
288
    double m) LLVM_NO_SANITIZE("float-cast-overflow");
289
290
static CallResult<PseudoHandle<StringPrimitive>> numberToString(
291
    Runtime &runtime,
292
228k
    double m) {
293
228k
  char buf8[hermes::NUMBER_TO_STRING_BUF_SIZE];
294
295
  // Optimization: Fast-case for positive integers < 2^31
296
228k
  int32_t n = static_cast<int32_t>(m);
297
228k
  if (m == static_cast<double>(n) && n > 0) {
298
    // Write base 10 digits in reverse from end of buf8.
299
194k
    char *p = buf8 + sizeof(buf8);
300
738k
    do {
301
738k
      *--p = '0' + (n % 10);
302
738k
      n /= 10;
303
738k
    } while (n);
304
194k
    size_t len = buf8 + sizeof(buf8) - p;
305
    // Temporarily stop the propagation of removing.
306
194k
    auto result = StringPrimitive::create(runtime, ASCIIRef(p, len));
307
194k
    if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
308
0
      return ExecutionStatus::EXCEPTION;
309
0
    }
310
194k
    return createPseudoHandle(vmcast<StringPrimitive>(*result));
311
194k
  }
312
313
34.0k
  auto getPredefined = [&runtime](Predefined::Str predefinedID) {
314
22.0k
    return createPseudoHandle(runtime.getPredefinedString(predefinedID));
315
22.0k
  };
316
317
34.0k
  if (std::isnan(m))
318
4
    return getPredefined(Predefined::NaN);
319
34.0k
  if (m == 0)
320
22.0k
    return getPredefined(Predefined::zero);
321
11.9k
  if (m == std::numeric_limits<double>::infinity())
322
0
    return getPredefined(Predefined::Infinity);
323
11.9k
  if (m == -std::numeric_limits<double>::infinity())
324
0
    return getPredefined(Predefined::NegativeInfinity);
325
326
  // After special cases, run the generic routine to convert.
327
11.9k
  size_t len = hermes::numberToString(m, buf8, sizeof(buf8));
328
329
11.9k
  auto result = StringPrimitive::create(runtime, ASCIIRef(buf8, len));
330
11.9k
  if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
331
0
    return ExecutionStatus::EXCEPTION;
332
0
  }
333
11.9k
  return createPseudoHandle(vmcast<StringPrimitive>(*result));
334
11.9k
}
335
336
CallResult<PseudoHandle<StringPrimitive>> toString_RJS(
337
    Runtime &runtime,
338
391k
    Handle<> valueHandle) {
339
391k
  HermesValue value = valueHandle.get();
340
391k
  StringPrimitive *result;
341
391k
  switch (value.getETag()) {
342
0
#ifdef HERMES_SLOW_DEBUG
343
0
    case HermesValue::ETag::Invalid:
344
0
      llvm_unreachable("invalid value");
345
0
#endif // HERMES_SLOW_DEBUG
346
0
    case HermesValue::ETag::Empty:
347
0
      llvm_unreachable("empty value");
348
0
    case HermesValue::ETag::Native1:
349
0
    case HermesValue::ETag::Native2:
350
0
      llvm_unreachable("native value");
351
0
    case HermesValue::ETag::BigInt1:
352
0
    case HermesValue::ETag::BigInt2: {
353
0
      const uint8_t kDefaultRadix = 10;
354
0
      auto res = BigIntPrimitive::toString(
355
0
          runtime, Handle<BigIntPrimitive>::vmcast(valueHandle), kDefaultRadix);
356
0
      if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
357
0
        return ExecutionStatus::EXCEPTION;
358
0
      }
359
0
      result = res->getString();
360
0
      break;
361
0
    }
362
162k
    case HermesValue::ETag::Str1:
363
162k
    case HermesValue::ETag::Str2:
364
162k
      result = vmcast<StringPrimitive>(value);
365
162k
      break;
366
202
    case HermesValue::ETag::Undefined:
367
202
      result = runtime.getPredefinedString(Predefined::undefined);
368
202
      break;
369
0
    case HermesValue::ETag::Null:
370
0
      result = runtime.getPredefinedString(Predefined::null);
371
0
      break;
372
0
    case HermesValue::ETag::Bool:
373
0
      result = value.getBool()
374
0
          ? runtime.getPredefinedString(Predefined::trueStr)
375
0
          : runtime.getPredefinedString(Predefined::falseStr);
376
0
      break;
377
173
    case HermesValue::ETag::Object1:
378
173
    case HermesValue::ETag::Object2: {
379
173
      auto res = toPrimitive_RJS(runtime, valueHandle, PreferredType::STRING);
380
173
      if (res == ExecutionStatus::EXCEPTION) {
381
0
        return ExecutionStatus::EXCEPTION;
382
0
      }
383
173
      return toString_RJS(runtime, runtime.makeHandle(res.getValue()));
384
173
    }
385
0
    case HermesValue::ETag::Symbol:
386
0
      return runtime.raiseTypeError("Cannot convert Symbol to string");
387
228k
    default:
388
228k
      return numberToString(runtime, value.getNumber());
389
391k
  }
390
391
162k
  return createPseudoHandle(result);
392
391k
}
393
394
0
double parseIntWithRadix(const StringView str, int radix) {
395
0
  auto res =
396
0
      hermes::parseIntWithRadix</* AllowNumericSeparator */ false>(str, radix);
397
0
  return res ? res.getValue() : std::numeric_limits<double>::quiet_NaN();
398
0
}
399
400
/// ES5.1 9.3.1
401
static inline double stringToNumber(
402
    Runtime &runtime,
403
88
    Handle<StringPrimitive> strPrim) {
404
88
  auto &idTable = runtime.getIdentifierTable();
405
406
  // Fast check for special values (no extraneous whitespace).
407
88
  if (runtime.symbolEqualsToStringPrim(
408
88
          Predefined::getSymbolID(Predefined::Infinity), *strPrim)) {
409
0
    return std::numeric_limits<double>::infinity();
410
0
  }
411
88
  if (runtime.symbolEqualsToStringPrim(
412
88
          Predefined::getSymbolID(Predefined::PositiveInfinity), *strPrim)) {
413
0
    return std::numeric_limits<double>::infinity();
414
0
  }
415
88
  if (runtime.symbolEqualsToStringPrim(
416
88
          Predefined::getSymbolID(Predefined::NegativeInfinity), *strPrim)) {
417
0
  }
418
88
  if (runtime.symbolEqualsToStringPrim(
419
88
          Predefined::getSymbolID(Predefined::NaN), *strPrim)) {
420
0
    return std::numeric_limits<double>::quiet_NaN();
421
0
  }
422
423
  // Trim string to the interval [begin, end).
424
88
  auto orig = StringPrimitive::createStringView(runtime, strPrim);
425
88
  auto begin = orig.begin();
426
88
  auto end = orig.end();
427
428
  // Move begin and end to ignore whitespace.
429
88
  while (begin != end &&
430
88
         (isWhiteSpaceChar(*begin) || isLineTerminatorChar(*begin))) {
431
0
    ++begin;
432
0
  }
433
88
  while (begin != end &&
434
88
         (isWhiteSpaceChar(*(end - 1)) || isLineTerminatorChar(*(end - 1)))) {
435
0
    --end;
436
0
  }
437
  // Early return for empty strings (strings only containing whitespace).
438
88
  if (begin == end) {
439
0
    return 0;
440
0
  }
441
442
  // Trim the string.
443
88
  StringView str16 = orig.slice(begin, end);
444
445
  // Slow check for special values.
446
  // This should only run if user created a string with extra whitespace,
447
  // since normal uses would get caught by the initial check.
448
88
  if (LLVM_UNLIKELY(str16.equals(idTable.getStringView(
449
88
          runtime, Predefined::getSymbolID(Predefined::Infinity))))) {
450
0
    return std::numeric_limits<double>::infinity();
451
0
  }
452
88
  if (LLVM_UNLIKELY(str16.equals(idTable.getStringView(
453
88
          runtime, Predefined::getSymbolID(Predefined::PositiveInfinity))))) {
454
0
    return std::numeric_limits<double>::infinity();
455
0
  }
456
88
  if (LLVM_UNLIKELY(str16.equals(idTable.getStringView(
457
88
          runtime, Predefined::getSymbolID(Predefined::NegativeInfinity))))) {
458
0
    return -std::numeric_limits<double>::infinity();
459
0
  }
460
88
  if (LLVM_UNLIKELY(str16.equals(idTable.getStringView(
461
88
          runtime, Predefined::getSymbolID(Predefined::NaN))))) {
462
0
    return std::numeric_limits<double>::quiet_NaN();
463
0
  }
464
465
88
  auto len = str16.length();
466
467
  // Parse hex codes, since dtoa doesn't do it.
468
  // FIXME: May be inaccurate for some hex values.
469
  // We need to check other sources first.
470
88
  if (len > 2) {
471
83
    if (str16[0] == u'0' && letterToLower(str16[1]) == u'x') {
472
0
      return parseIntWithRadix(str16.slice(2), 16);
473
0
    }
474
83
    if (str16[0] == u'0' && letterToLower(str16[1]) == u'o') {
475
0
      return parseIntWithRadix(str16.slice(2), 8);
476
0
    }
477
83
    if (str16[0] == u'0' && letterToLower(str16[1]) == u'b') {
478
0
      return parseIntWithRadix(str16.slice(2), 2);
479
0
    }
480
83
  }
481
482
  // Finally, copy 16 bit chars into 8 bit chars and call dtoa.
483
88
  llvh::SmallVector<char, 32> str8(len + 1);
484
88
  uint32_t i = 0;
485
412
  for (auto c16 : str16) {
486
    // Check to ensure we only have valid number characters now.
487
412
    if ((u'0' <= c16 && c16 <= u'9') || c16 == u'.' ||
488
412
        letterToLower(c16) == u'e' || c16 == u'+' || c16 == u'-') {
489
406
      str8[i] = static_cast<char>(c16);
490
406
    } else {
491
6
      return std::numeric_limits<double>::quiet_NaN();
492
6
    }
493
406
    ++i;
494
406
  }
495
82
  str8[len] = '\0';
496
82
  char *endPtr;
497
82
  double result = ::hermes_g_strtod(str8.data(), &endPtr);
498
82
  if (endPtr == str8.data() + len) {
499
82
    return result;
500
82
  }
501
502
  // If everything failed, return NaN.
503
0
  return std::numeric_limits<double>::quiet_NaN();
504
82
}
505
506
688
CallResult<HermesValue> toNumber_RJS(Runtime &runtime, Handle<> valueHandle) {
507
688
  auto value = valueHandle.get();
508
688
  double result;
509
688
  switch (value.getETag()) {
510
0
#ifdef HERMES_SLOW_DEBUG
511
0
    case HermesValue::ETag::Invalid:
512
0
      llvm_unreachable("invalid value");
513
0
#endif // HERMES_SLOW_DEBUG
514
0
    case HermesValue::ETag::Empty:
515
0
      llvm_unreachable("empty value");
516
0
    case HermesValue::ETag::Native1:
517
0
    case HermesValue::ETag::Native2:
518
0
      llvm_unreachable("native value");
519
81
    case HermesValue::ETag::Object1:
520
81
    case HermesValue::ETag::Object2: {
521
81
      auto res = toPrimitive_RJS(runtime, valueHandle, PreferredType::NUMBER);
522
81
      if (res == ExecutionStatus::EXCEPTION) {
523
0
        return ExecutionStatus::EXCEPTION;
524
0
      }
525
81
      return toNumber_RJS(runtime, runtime.makeHandle(res.getValue()));
526
81
    }
527
88
    case HermesValue::ETag::Str1:
528
88
    case HermesValue::ETag::Str2:
529
88
      result =
530
88
          stringToNumber(runtime, Handle<StringPrimitive>::vmcast(valueHandle));
531
88
      break;
532
0
    case HermesValue::ETag::BigInt1:
533
0
    case HermesValue::ETag::BigInt2:
534
0
      return runtime.raiseTypeError("Cannot convert BigInt to number");
535
0
    case HermesValue::ETag::Undefined:
536
0
      result = std::numeric_limits<double>::quiet_NaN();
537
0
      break;
538
0
    case HermesValue::ETag::Null:
539
0
      result = +0.0;
540
0
      break;
541
0
    case HermesValue::ETag::Bool:
542
0
      result = value.getBool();
543
0
      break;
544
0
    case HermesValue::ETag::Symbol:
545
0
      return runtime.raiseTypeError("Cannot convert Symbol to number");
546
519
    default:
547
      // Already have a number, just return it.
548
519
      return value;
549
688
  }
550
88
  return HermesValue::encodeUntrustedNumberValue(result);
551
688
}
552
553
3
CallResult<HermesValue> toNumeric_RJS(Runtime &runtime, Handle<> valueHandle) {
554
3
  GCScopeMarkerRAII marker{runtime};
555
3
  auto primValue = toPrimitive_RJS(runtime, valueHandle, PreferredType::NUMBER);
556
557
3
  if (LLVM_UNLIKELY(primValue == ExecutionStatus::EXCEPTION)) {
558
0
    return ExecutionStatus::EXCEPTION;
559
0
  }
560
561
3
  if (primValue->isBigInt()) {
562
0
    return *primValue;
563
0
  }
564
565
3
  return toNumber_RJS(runtime, runtime.makeHandle(*primValue));
566
3
}
567
568
0
CallResult<HermesValue> toLength(Runtime &runtime, Handle<> valueHandle) {
569
0
  constexpr double maxLength = 9007199254740991.0; // 2**53 - 1
570
0
  auto res = toIntegerOrInfinity(runtime, valueHandle);
571
0
  if (res == ExecutionStatus::EXCEPTION) {
572
0
    return ExecutionStatus::EXCEPTION;
573
0
  }
574
0
  auto len = res->getNumber();
575
0
  if (len <= 0) {
576
0
    len = 0;
577
0
  } else if (len > maxLength) {
578
0
    len = maxLength;
579
0
  }
580
0
  return HermesValue::encodeUntrustedNumberValue(len);
581
0
}
582
583
335
CallResult<uint64_t> toLengthU64(Runtime &runtime, Handle<> valueHandle) {
584
335
  constexpr double highestIntegralDouble =
585
335
      ((uint64_t)1 << std::numeric_limits<double>::digits) - 1;
586
335
  auto res = toIntegerOrInfinity(runtime, valueHandle);
587
335
  if (res == ExecutionStatus::EXCEPTION) {
588
0
    return ExecutionStatus::EXCEPTION;
589
0
  }
590
335
  auto len = res->getNumber();
591
335
  if (len <= 0) {
592
79
    len = 0;
593
256
  } else if (len > highestIntegralDouble) {
594
0
    len = highestIntegralDouble;
595
0
  }
596
335
  return len;
597
335
}
598
599
0
CallResult<HermesValue> toIndex(Runtime &runtime, Handle<> valueHandle) {
600
0
  auto value = (valueHandle->isUndefined())
601
0
      ? runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(0))
602
0
      : valueHandle;
603
0
  auto res = toIntegerOrInfinity(runtime, value);
604
0
  if (res == ExecutionStatus::EXCEPTION) {
605
0
    return ExecutionStatus::EXCEPTION;
606
0
  }
607
0
  auto integerIndex = res->getNumber();
608
0
  if (integerIndex < 0) {
609
0
    return runtime.raiseRangeError("A negative value cannot be an index");
610
0
  }
611
0
  auto integerIndexHandle =
612
0
      runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(integerIndex));
613
0
  res = toLength(runtime, integerIndexHandle);
614
0
  if (res == ExecutionStatus::EXCEPTION) {
615
0
    return ExecutionStatus::EXCEPTION;
616
0
  }
617
0
  auto index = res.getValue();
618
0
  if (index.getNumber() != integerIndex) {
619
0
    return runtime.raiseRangeError(
620
0
        "The value given for the index must be between 0 and 2 ^ 53 - 1");
621
0
  }
622
0
  return res;
623
0
}
624
625
CallResult<HermesValue> toIntegerOrInfinity(
626
    Runtime &runtime,
627
521
    Handle<> valueHandle) {
628
521
  auto res = toNumber_RJS(runtime, valueHandle);
629
521
  if (res == ExecutionStatus::EXCEPTION) {
630
0
    return ExecutionStatus::EXCEPTION;
631
0
  }
632
521
  double num = res->getNumber();
633
634
521
  double result;
635
521
  if (std::isnan(num)) {
636
0
    result = 0;
637
521
  } else {
638
521
    result = std::trunc(num);
639
521
  }
640
641
521
  return HermesValue::encodeTrustedNumberValue(result);
642
521
}
643
644
/// Conversion of HermesValues to integers.
645
template <typename T>
646
static inline CallResult<HermesValue> toInt(
647
    Runtime &runtime,
648
79
    Handle<> valueHandle) {
649
79
  auto res = toNumber_RJS(runtime, valueHandle);
650
79
  if (res == ExecutionStatus::EXCEPTION) {
651
0
    return ExecutionStatus::EXCEPTION;
652
0
  }
653
79
  double num = res->getNumber();
654
79
  T result = static_cast<T>(hermes::truncateToInt32(num));
655
79
  return HermesValue::encodeUntrustedNumberValue(result);
656
79
}
Unexecuted instantiation: Operations.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::toInt<signed char>(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>)
Unexecuted instantiation: Operations.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::toInt<short>(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>)
Unexecuted instantiation: Operations.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::toInt<int>(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>)
Unexecuted instantiation: Operations.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::toInt<unsigned char>(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>)
Unexecuted instantiation: Operations.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::toInt<unsigned short>(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>)
Operations.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::toInt<unsigned int>(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>)
Line
Count
Source
648
79
    Handle<> valueHandle) {
649
79
  auto res = toNumber_RJS(runtime, valueHandle);
650
79
  if (res == ExecutionStatus::EXCEPTION) {
651
0
    return ExecutionStatus::EXCEPTION;
652
0
  }
653
79
  double num = res->getNumber();
654
79
  T result = static_cast<T>(hermes::truncateToInt32(num));
655
79
  return HermesValue::encodeUntrustedNumberValue(result);
656
79
}
657
658
0
CallResult<HermesValue> toInt8(Runtime &runtime, Handle<> valueHandle) {
659
0
  return toInt<int8_t>(runtime, valueHandle);
660
0
}
661
662
0
CallResult<HermesValue> toInt16(Runtime &runtime, Handle<> valueHandle) {
663
0
  return toInt<int16_t>(runtime, valueHandle);
664
0
}
665
666
0
CallResult<HermesValue> toInt32_RJS(Runtime &runtime, Handle<> valueHandle) {
667
0
  return toInt<int32_t>(runtime, valueHandle);
668
0
}
669
670
0
CallResult<HermesValue> toUInt8(Runtime &runtime, Handle<> valueHandle) {
671
0
  return toInt<uint8_t>(runtime, valueHandle);
672
0
}
673
674
0
uint8_t toUInt8Clamp(double number) {
675
  // 3. If number is NaN, return +0.
676
  // 4. If number <= 0, return +0.
677
  // Not < so that NaN coerces to 0.
678
  // NOTE: this check correctly rounds numbers less than 0.5
679
0
  if (!(number >= 0.5)) {
680
0
    return 0;
681
0
  }
682
683
  // 5. If number >= 255, return 255.
684
0
  if (number > 255) {
685
0
    return 255;
686
0
  }
687
688
  // The next steps are the equivalent of the spec's round-to-even requirement.
689
  // Round up and then do the even/odd check.
690
0
  double toTruncate = number + 0.5;
691
0
  uint8_t x = static_cast<uint8_t>(toTruncate);
692
693
  // If it was a tie (i.e. it ended in 0.5) then
694
0
  if (x == toTruncate) {
695
    // number ended in 0.5 and was rounded up, reduce by 1 if odd,
696
    // else leave the same.
697
    // That is the same as unsetting the least significant bit.
698
0
    return (x & ~1);
699
0
  } else {
700
    // number did not end in 0.5, don't need to check the parity.
701
0
    return x;
702
0
  }
703
0
}
704
705
0
CallResult<HermesValue> toUInt8Clamp(Runtime &runtime, Handle<> valueHandle) {
706
  // 1. Let number be toNumber_RJS(argument)
707
0
  auto res = toNumber_RJS(runtime, valueHandle);
708
0
  if (res == ExecutionStatus::EXCEPTION) {
709
    // 2. ReturnIfAbrupt(number)
710
0
    return ExecutionStatus::EXCEPTION;
711
0
  }
712
0
  return HermesValue::encodeUntrustedNumberValue(
713
0
      toUInt8Clamp(res->getNumber()));
714
0
}
715
716
0
CallResult<HermesValue> toUInt16(Runtime &runtime, Handle<> valueHandle) {
717
0
  return toInt<uint16_t>(runtime, valueHandle);
718
0
}
719
720
79
CallResult<HermesValue> toUInt32_RJS(Runtime &runtime, Handle<> valueHandle) {
721
79
  return toInt<uint32_t>(runtime, valueHandle);
722
79
}
723
724
CallResult<Handle<JSObject>> getPrimitivePrototype(
725
    Runtime &runtime,
726
165
    Handle<> base) {
727
165
  switch (base->getETag()) {
728
0
#ifdef HERMES_SLOW_DEBUG
729
0
    case HermesValue::ETag::Invalid:
730
0
      llvm_unreachable("invalid value");
731
0
#endif // HERMES_SLOW_DEBUG
732
0
    case HermesValue::ETag::Empty:
733
0
      llvm_unreachable("empty value");
734
0
    case HermesValue::ETag::Native1:
735
0
    case HermesValue::ETag::Native2:
736
0
      llvm_unreachable("native value");
737
0
    case HermesValue::ETag::Object1:
738
0
    case HermesValue::ETag::Object2:
739
0
      llvm_unreachable("object value");
740
3
    case HermesValue::ETag::Undefined:
741
3
      return runtime.raiseTypeError("Cannot convert undefined value to object");
742
0
    case HermesValue::ETag::Null:
743
0
      return runtime.raiseTypeError("Cannot convert null value to object");
744
162
    case HermesValue::ETag::Str1:
745
162
    case HermesValue::ETag::Str2:
746
162
      return Handle<JSObject>::vmcast(&runtime.stringPrototype);
747
0
    case HermesValue::ETag::BigInt1:
748
0
    case HermesValue::ETag::BigInt2:
749
0
      return Handle<JSObject>::vmcast(&runtime.bigintPrototype);
750
0
    case HermesValue::ETag::Bool:
751
0
      return Handle<JSObject>::vmcast(&runtime.booleanPrototype);
752
0
    case HermesValue::ETag::Symbol:
753
0
      return Handle<JSObject>::vmcast(&runtime.symbolPrototype);
754
0
    default:
755
0
      assert(base->isNumber() && "Unknown tag in getPrimitivePrototype.");
756
0
      return Handle<JSObject>::vmcast(&runtime.numberPrototype);
757
165
  }
758
165
}
759
760
1.40k
CallResult<HermesValue> toObject(Runtime &runtime, Handle<> valueHandle) {
761
1.40k
  auto value = valueHandle.get();
762
1.40k
  switch (value.getETag()) {
763
0
#ifdef HERMES_SLOW_DEBUG
764
0
    case HermesValue::ETag::Invalid:
765
0
      llvm_unreachable("invalid value");
766
0
#endif // HERMES_SLOW_DEBUG
767
0
    case HermesValue::ETag::Empty:
768
0
      llvm_unreachable("empty value");
769
0
    case HermesValue::ETag::Native1:
770
0
    case HermesValue::ETag::Native2:
771
0
      llvm_unreachable("native value");
772
0
    case HermesValue::ETag::Undefined:
773
0
      return runtime.raiseTypeError("Cannot convert undefined value to object");
774
0
    case HermesValue::ETag::Null:
775
0
      return runtime.raiseTypeError("Cannot convert null value to object");
776
1.40k
    case HermesValue::ETag::Object1:
777
1.40k
    case HermesValue::ETag::Object2:
778
1.40k
      return value;
779
0
    case HermesValue::ETag::Bool:
780
0
      return JSBoolean::create(
781
0
                 runtime,
782
0
                 value.getBool(),
783
0
                 Handle<JSObject>::vmcast(&runtime.booleanPrototype))
784
0
          .getHermesValue();
785
0
    case HermesValue::ETag::BigInt1:
786
0
    case HermesValue::ETag::BigInt2:
787
0
      return JSBigInt::create(
788
0
                 runtime,
789
0
                 Handle<BigIntPrimitive>::vmcast(valueHandle),
790
0
                 Handle<JSObject>::vmcast(&runtime.bigintPrototype))
791
0
          .getHermesValue();
792
0
    case HermesValue::ETag::Str1:
793
0
    case HermesValue::ETag::Str2: {
794
0
      auto res = JSString::create(
795
0
          runtime,
796
0
          Handle<StringPrimitive>::vmcast(valueHandle),
797
0
          Handle<JSObject>::vmcast(&runtime.stringPrototype));
798
0
      if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
799
0
        return ExecutionStatus::EXCEPTION;
800
0
      }
801
0
      return res->getHermesValue();
802
0
    }
803
0
    case HermesValue::ETag::Symbol:
804
0
      return JSSymbol::create(
805
0
                 runtime,
806
0
                 *Handle<SymbolID>::vmcast(valueHandle),
807
0
                 Handle<JSObject>::vmcast(&runtime.symbolPrototype))
808
0
          .getHermesValue();
809
3
    default:
810
3
      assert(valueHandle->isNumber() && "Unknown tag in toObject.");
811
0
      return JSNumber::create(
812
3
                 runtime,
813
3
                 value.getNumber(),
814
3
                 Handle<JSObject>::vmcast(&runtime.numberPrototype))
815
3
          .getHermesValue();
816
1.40k
  }
817
1.40k
}
818
819
ExecutionStatus amendPropAccessErrorMsgWithPropName(
820
    Runtime &runtime,
821
    Handle<> valueHandle,
822
    llvh::StringRef operationStr,
823
3
    SymbolID id) {
824
3
  if (!valueHandle->isNull() && !valueHandle->isUndefined()) {
825
    // If value is not null/undefined, fall back to the original exception.
826
0
    return ExecutionStatus::EXCEPTION;
827
0
  }
828
3
  assert(!runtime.getThrownValue().isEmpty() && "Error must have been thrown");
829
  // Clear the error first because we will re-throw.
830
0
  runtime.clearThrownValue();
831
832
  // Construct an error message that contains the property name.
833
3
  llvh::StringRef valueStr = valueHandle->isNull() ? "null" : "undefined";
834
3
  return runtime.raiseTypeError(
835
3
      TwineChar16("Cannot ") + operationStr + " property '" +
836
3
      runtime.getIdentifierTable().getStringView(runtime, id) + "' of " +
837
3
      valueStr);
838
3
}
839
840
/// Implement a BigInt vs. String comparison operation using a user-provided
841
/// \p comparator. Note that \p leftHandle is a Handle<BigIntPrimitive> to
842
/// ensure the caller is putting the BigInt in the lhs (and adjusting \p
843
/// comparator appropriately).
844
/// \return false if StringToBigInt( \p rightHandle ) is undefined, otherwise
845
/// returns \p comparator ( \p leftHandle <=> \p righHandle ).
846
static CallResult<bool> compareBigIntAndString(
847
    Runtime &runtime,
848
    Handle<BigIntPrimitive> leftHandle,
849
    Handle<> rightHandle,
850
0
    bool (*comparator)(int)) {
851
0
  assert(rightHandle->isString() && "rhs should be string");
852
853
0
  auto bigintRight = stringToBigInt(runtime, rightHandle);
854
0
  if (LLVM_UNLIKELY(bigintRight == ExecutionStatus::EXCEPTION)) {
855
0
    return ExecutionStatus::EXCEPTION;
856
0
  }
857
0
  if (bigintRight->isUndefined()) { // Non-compliance: should be undefined.
858
0
    return false;
859
0
  }
860
0
  assert(bigintRight->isBigInt() && "stringToBigInt resulted in non-bigint");
861
0
  return comparator(leftHandle->compare(bigintRight->getBigInt()));
862
0
}
863
864
/// Implement a BigInt vs. Number comparison operation using a user-provided
865
/// \p comparator. Note that \p leftHandle is a Handle<BigIntPrimitive> to
866
/// ensure the caller is putting the BigInt in the lhs (and adjusting \p
867
/// comparator appropriately).
868
/// \return false if \p right is NaN, otherwise returns
869
/// \p comparator ( \p leftHandle <=> \p righHandle ).
870
static CallResult<bool> compareBigIntAndNumber(
871
    Runtime &runtime,
872
    Handle<BigIntPrimitive> leftHandle,
873
    double right,
874
0
    bool (*comparator)(int)) {
875
0
  switch (std::fpclassify(right)) {
876
0
    case FP_NAN:
877
      // BigInt comparison to NaN is always false.
878
0
      return false;
879
0
    case FP_INFINITE:
880
      // If rhs is +infinite, it is greater than lhs; otherwise, it is less than
881
      // rhs.
882
0
      return comparator(right > 0 ? -1 : 1);
883
0
    default:
884
0
      break;
885
0
  }
886
887
  // Split the rhs into integral and fractional parts.
888
0
  double integralPart;
889
0
  const double fractionalPart = std::modf(right, &integralPart);
890
891
  // Now use the rhs' integral part to create a new BigInt, which is compared to
892
  // lhs.
893
0
  auto rightHandle = BigIntPrimitive::fromDouble(runtime, integralPart);
894
0
  if (LLVM_UNLIKELY(rightHandle == ExecutionStatus::EXCEPTION)) {
895
0
    return ExecutionStatus::EXCEPTION;
896
0
  }
897
898
  // If rhs' integral part is different than lhs, then use the integral parts'
899
  // comparison to decide the result.
900
0
  if (int comparisonResult = leftHandle->compare(rightHandle->getBigInt())) {
901
0
    return comparator(comparisonResult);
902
0
  }
903
904
  // Lhs' and rhs' integral parts are equal, thus resort the rhs' fractional
905
  // part.
906
0
  if (fractionalPart != 0) {
907
    //  If rhs is negative, then it is smaller than lhs; otherwise, it is
908
    //  greater.
909
0
    return comparator(right < 0 ? 1 : -1);
910
0
  }
911
912
  // Lhs' and rhs' integral parts are equal, and rhs does not have a fractional
913
  // part (it is zero), thus they are equal.
914
0
  return comparator(0);
915
0
}
916
917
/// Implement a comparison operator. First both operands a converted to
918
/// primitives. If they both end up being strings, a lexicographical comparison
919
/// is performed. Otherwise both operands are converted to numbers and the
920
/// values are compared.
921
/// \param oper is the comparison operator to use when comparing numbers.
922
#define IMPLEMENT_COMPARISON_OP(name, oper)                           \
923
  CallResult<bool> name(                                              \
924
0
      Runtime &runtime, Handle<> leftHandle, Handle<> rightHandle) {  \
925
0
    auto resLeft =                                                    \
926
0
        toPrimitive_RJS(runtime, leftHandle, PreferredType::NUMBER);  \
927
0
    if (resLeft == ExecutionStatus::EXCEPTION)                        \
928
0
      return ExecutionStatus::EXCEPTION;                              \
929
0
    MutableHandle<> left(runtime, resLeft.getValue());                \
930
0
                                                                      \
931
0
    auto resRight =                                                   \
932
0
        toPrimitive_RJS(runtime, rightHandle, PreferredType::NUMBER); \
933
0
    if (resRight == ExecutionStatus::EXCEPTION)                       \
934
0
      return ExecutionStatus::EXCEPTION;                              \
935
0
    MutableHandle<> right(runtime, resRight.getValue());              \
936
0
                                                                      \
937
0
    /* If both are strings, we must do a string comparison.*/         \
938
0
    if (left->isString() && right->isString()) {                      \
939
0
      return left->getString()->compare(right->getString()) oper 0;   \
940
0
    }                                                                 \
941
0
                                                                      \
942
0
    if (left->isBigInt() && right->isString()) {                      \
943
0
      return compareBigIntAndString(                                  \
944
0
          runtime,                                                    \
945
0
          Handle<BigIntPrimitive>::vmcast(left),                      \
946
0
          right,                                                      \
947
0
          [](int result) { return result oper 0; });                  \
Unexecuted instantiation: Operations.cpp:hermes::vm::lessOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_0::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_4::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::lessEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_8::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_12::operator()(int) const
948
0
    }                                                                 \
949
0
                                                                      \
950
0
    if (left->isString() && right->isBigInt()) {                      \
951
0
      return compareBigIntAndString(                                  \
952
0
          runtime,                                                    \
953
0
          Handle<BigIntPrimitive>::vmcast(right),                     \
954
0
          left,                                                       \
955
0
          [](int result) { return -result oper 0; });                 \
Unexecuted instantiation: Operations.cpp:hermes::vm::lessOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_1::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_5::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::lessEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_9::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_13::operator()(int) const
956
0
    }                                                                 \
957
0
                                                                      \
958
0
    /* Convert both to a number and compare the numbers. */           \
959
0
    resLeft = toNumeric_RJS(runtime, left);                           \
960
0
    if (resLeft == ExecutionStatus::EXCEPTION)                        \
961
0
      return ExecutionStatus::EXCEPTION;                              \
962
0
    left = resLeft.getValue();                                        \
963
0
    resRight = toNumeric_RJS(runtime, right);                         \
964
0
    if (resRight == ExecutionStatus::EXCEPTION)                       \
965
0
      return ExecutionStatus::EXCEPTION;                              \
966
0
    right = resRight.getValue();                                      \
967
0
                                                                      \
968
0
    if (left->isNumber() && right->isNumber()) {                      \
969
0
      return left->getNumber() oper right->getNumber();               \
970
0
    } else if (left->isBigInt() && right->isBigInt()) {               \
971
0
      return left->getBigInt()->compare(right->getBigInt()) oper 0;   \
972
0
    }                                                                 \
973
0
                                                                      \
974
0
    if (left->isBigInt() && right->isNumber()) {                      \
975
0
      return compareBigIntAndNumber(                                  \
976
0
          runtime,                                                    \
977
0
          Handle<BigIntPrimitive>::vmcast(left),                      \
978
0
          right->getNumber(),                                         \
979
0
          [](int result) { return result oper 0; });                  \
Unexecuted instantiation: Operations.cpp:hermes::vm::lessOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_2::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_6::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::lessEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_10::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_14::operator()(int) const
980
0
    }                                                                 \
981
0
    assert(                                                           \
982
0
        left->isNumber() && right->isBigInt() &&                      \
983
0
        "expecting one number and one bigint");                       \
984
0
    return compareBigIntAndNumber(                                    \
985
0
        runtime,                                                      \
986
0
        Handle<BigIntPrimitive>::vmcast(right),                       \
987
0
        left->getNumber(),                                            \
988
0
        [](int result) { return -result oper 0; });                   \
Unexecuted instantiation: Operations.cpp:hermes::vm::lessOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_3::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_7::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::lessEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_11::operator()(int) const
Unexecuted instantiation: Operations.cpp:hermes::vm::greaterEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)::$_15::operator()(int) const
989
0
  }
Unexecuted instantiation: hermes::vm::lessOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)
Unexecuted instantiation: hermes::vm::greaterOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)
Unexecuted instantiation: hermes::vm::lessEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)
Unexecuted instantiation: hermes::vm::greaterEqualOp_RJS(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::HermesValue>, hermes::vm::Handle<hermes::vm::HermesValue>)
990
991
IMPLEMENT_COMPARISON_OP(lessOp_RJS, <);
992
IMPLEMENT_COMPARISON_OP(greaterOp_RJS, >);
993
IMPLEMENT_COMPARISON_OP(lessEqualOp_RJS, <=);
994
IMPLEMENT_COMPARISON_OP(greaterEqualOp_RJS, >=);
995
996
/// ES11 7.2.15 Abstract Equality Comparison
997
CallResult<bool>
998
630
abstractEqualityTest_RJS(Runtime &runtime, Handle<> xHandle, Handle<> yHandle) {
999
630
  MutableHandle<> x{runtime, xHandle.get()};
1000
630
  MutableHandle<> y{runtime, yHandle.get()};
1001
1002
630
  while (true) {
1003
    // Combine tags for use in the switch statement. Use NativeValueTag as a
1004
    // placeholder for numbers.
1005
630
    assert(
1006
630
        !x->isNativeValue() && !x->isEmpty() && "invalid value for comparison");
1007
0
    assert(
1008
630
        !y->isNativeValue() && !y->isEmpty() && "invalid value for comparison");
1009
1010
    // The following macros are used to generate the switch cases using
1011
    // HermesValue::combineETags; an S in the name means it is a single ETag
1012
    // (e.g., ETag::Bool), while M means it is a multi ETag (e.g., ETag::Object1
1013
    // and ETag::Object2).
1014
0
#define CASE_S_S(typeA, typeB)    \
1015
0
  case HermesValue::combineETags( \
1016
0
      HermesValue::ETag::typeA, HermesValue::ETag::typeB):
1017
1018
0
#define CASE_S_M(typeA, typeB) \
1019
0
  CASE_S_S(typeA, typeB##1)    \
1020
0
  CASE_S_S(typeA, typeB##2)
1021
1022
0
#define CASE_M_S(typeA, typeB) \
1023
0
  CASE_S_S(typeA##1, typeB)    \
1024
0
  CASE_S_S(typeA##2, typeB)
1025
1026
0
#define CASE_M_M(typeA, typeB) \
1027
0
  CASE_M_S(typeA, typeB##1)    \
1028
0
  CASE_M_S(typeA, typeB##2)
1029
1030
// NUMBER_TAG is a "virtual" ETag member that is used to tag numbers (which
1031
// don't have a tag assigned to them). It reuses ETag::Native1 there will
1032
// never be any native values in this part of the code.
1033
0
#define NUMBER_TAG Native1
1034
1035
    // Tag numbers as with the "virtual" ETag member NUMBER_TAG, and use default
1036
    // tag values for everything else.
1037
0
    HermesValue::ETag xType =
1038
630
        x->isNumber() ? HermesValue::ETag::NUMBER_TAG : x->getETag();
1039
630
    HermesValue::ETag yType =
1040
630
        y->isNumber() ? HermesValue::ETag::NUMBER_TAG : y->getETag();
1041
1042
630
    switch (HermesValue::combineETags(xType, yType)) {
1043
      // 1. If Type(x) is the same as Type(y), then
1044
      // a. Return the result of performing Strict Equality Comparison x === y.
1045
0
      CASE_S_S(Undefined, Undefined)
1046
0
      CASE_S_S(Null, Null) {
1047
0
        return true;
1048
0
      }
1049
0
      CASE_S_S(NUMBER_TAG, NUMBER_TAG) {
1050
0
        return x->getNumber() == y->getNumber();
1051
0
      }
1052
0
      CASE_M_M(Str, Str) {
1053
0
        return x->getString()->equals(y->getString());
1054
0
      }
1055
0
      CASE_M_M(BigInt, BigInt) {
1056
0
        return x->getBigInt()->compare(y->getBigInt()) == 0;
1057
0
      }
1058
0
      CASE_S_S(Bool, Bool)
1059
0
      CASE_S_S(Symbol, Symbol)
1060
0
      CASE_M_M(Object, Object) {
1061
0
        return x->getRaw() == y->getRaw();
1062
0
      }
1063
      // 2. If x is null and y is undefined, return true.
1064
      // 3. If x is undefined and y is null, return true.
1065
0
      CASE_S_S(Undefined, Null)
1066
0
      CASE_S_S(Null, Undefined) {
1067
0
        return true;
1068
0
      }
1069
      // 4. If Type(x) is Number and Type(y) is String, return the result of the
1070
      // comparison x == ! ToNumber(y).
1071
0
      CASE_S_M(NUMBER_TAG, Str) {
1072
0
        return x->getNumber() ==
1073
0
            stringToNumber(runtime, Handle<StringPrimitive>::vmcast(y));
1074
0
      }
1075
      // 5. If Type(x) is String and Type(y) is Number, return the result of the
1076
      // comparison ! ToNumber(x) == y.
1077
0
      CASE_M_S(Str, NUMBER_TAG) {
1078
0
        return stringToNumber(runtime, Handle<StringPrimitive>::vmcast(x)) ==
1079
0
            y->getNumber();
1080
0
      }
1081
      // 6. If Type(x) is BigInt and Type(y) is String, then
1082
0
      CASE_M_M(BigInt, Str) {
1083
        // a. Let n be ! StringToBigInt(y).
1084
0
        auto n = stringToBigInt(runtime, y);
1085
0
        if (LLVM_UNLIKELY(n == ExecutionStatus::EXCEPTION)) {
1086
0
          return ExecutionStatus::EXCEPTION;
1087
0
        }
1088
        // b. If n is NaN, return false.
1089
        // N.B.: this has been amended in ES2023 to read
1090
        //       If n is undefined, return false.
1091
0
        if (n->isUndefined()) {
1092
0
          return false;
1093
0
        }
1094
        // c. Return the result of the comparison x == n.
1095
0
        y = n.getValue();
1096
0
        break;
1097
0
      }
1098
      // 7. If Type(x) is String and Type(y) is BigInt, return the result of the
1099
      // comparison y == x.
1100
0
      CASE_M_M(Str, BigInt) {
1101
0
        std::swap(x, y);
1102
0
        break;
1103
0
      }
1104
      // 8. If Type(x) is Boolean, return the result of the comparison !
1105
      // ToNumber(x) == y.
1106
0
      CASE_S_S(Bool, NUMBER_TAG) {
1107
        // Do both conversions and check numerical equality.
1108
0
        return static_cast<double>(x->getBool()) == y->getNumber();
1109
0
      }
1110
0
      CASE_S_M(Bool, Str) {
1111
        // Do string parsing and check double equality.
1112
0
        return static_cast<double>(x->getBool()) ==
1113
0
            stringToNumber(runtime, Handle<StringPrimitive>::vmcast(y));
1114
0
      }
1115
0
      CASE_S_M(Bool, BigInt) {
1116
0
        return y->getBigInt()->compare(static_cast<int32_t>(x->getBool())) == 0;
1117
0
      }
1118
0
      CASE_S_M(Bool, Object) {
1119
0
        x = HermesValue::encodeUntrustedNumberValue(x->getBool());
1120
0
        break;
1121
0
      }
1122
      // 9. If Type(y) is Boolean, return the result of the comparison x == !
1123
      // ToNumber(y).
1124
0
      CASE_S_S(NUMBER_TAG, Bool) {
1125
0
        return x->getNumber() == static_cast<double>(y->getBool());
1126
0
      }
1127
0
      CASE_M_S(Str, Bool) {
1128
0
        return stringToNumber(runtime, Handle<StringPrimitive>::vmcast(x)) ==
1129
0
            static_cast<double>(y->getBool());
1130
0
      }
1131
0
      CASE_M_S(BigInt, Bool) {
1132
0
        return x->getBigInt()->compare(static_cast<int32_t>(y->getBool())) == 0;
1133
0
      }
1134
0
      CASE_M_S(Object, Bool) {
1135
0
        y = HermesValue::encodeUntrustedNumberValue(y->getBool());
1136
0
        break;
1137
0
      }
1138
      // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y)
1139
      // is Object, return the result of the comparison x == ToPrimitive(y).
1140
0
      CASE_M_M(Str, Object)
1141
0
      CASE_M_M(BigInt, Object)
1142
0
      CASE_S_M(Symbol, Object)
1143
0
      CASE_S_M(NUMBER_TAG, Object) {
1144
0
        auto status = toPrimitive_RJS(runtime, y, PreferredType::NONE);
1145
0
        if (status == ExecutionStatus::EXCEPTION) {
1146
0
          return ExecutionStatus::EXCEPTION;
1147
0
        }
1148
0
        y = status.getValue();
1149
0
        break;
1150
0
      }
1151
      // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt,
1152
      // or Symbol, return the result of the comparison ToPrimitive(x) == y.
1153
0
      CASE_M_M(Object, Str)
1154
0
      CASE_M_M(Object, BigInt)
1155
0
      CASE_M_S(Object, Symbol)
1156
0
      CASE_M_S(Object, NUMBER_TAG) {
1157
0
        auto status = toPrimitive_RJS(runtime, x, PreferredType::NONE);
1158
0
        if (status == ExecutionStatus::EXCEPTION) {
1159
0
          return ExecutionStatus::EXCEPTION;
1160
0
        }
1161
0
        x = status.getValue();
1162
0
        break;
1163
0
      }
1164
      // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is
1165
      // Number and Type(y) is BigInt, then a. If x or y are any of NaN, +∞,
1166
      // or -∞, return false. b. If the mathematical value of x is equal to
1167
      // the mathematical value of y, return true; otherwise return false.
1168
0
      CASE_M_S(BigInt, NUMBER_TAG) {
1169
0
        std::swap(x, y);
1170
0
        [[fallthrough]];
1171
0
      }
1172
0
      CASE_S_M(NUMBER_TAG, BigInt) {
1173
0
        if (!isIntegralNumber(x->getNumber())) {
1174
0
          return false;
1175
0
        }
1176
1177
0
        auto xAsBigInt = BigIntPrimitive::fromDouble(runtime, x->getNumber());
1178
0
        if (LLVM_UNLIKELY(xAsBigInt == ExecutionStatus::EXCEPTION)) {
1179
0
          return ExecutionStatus::EXCEPTION;
1180
0
        }
1181
0
        return xAsBigInt->getBigInt()->compare(y->getBigInt()) == 0;
1182
0
      }
1183
1184
      // 13. Return false.
1185
630
      default:
1186
630
        return false;
1187
630
    }
1188
1189
630
#undef CASE_S_S
1190
630
#undef CASE_S_M
1191
630
#undef CASE_M_S
1192
630
#undef CASE_M_M
1193
630
#undef NUMBER_TAG
1194
630
  }
1195
630
}
1196
1197
1.89k
bool strictEqualityTest(HermesValue x, HermesValue y) {
1198
  // Numbers are special because they can have different tags and they don't
1199
  // obey bit-exact equality (because of NaN).
1200
1.89k
  if (x.isNumber())
1201
0
    return y.isNumber() && x.getNumber() == y.getNumber();
1202
  // If they are not numbers and are bit exact, they must be the same.
1203
1.89k
  if (x.getRaw() == y.getRaw())
1204
1.89k
    return true;
1205
  // All the rest of the cases need to have the same tags.
1206
0
  if (x.getTag() != y.getTag())
1207
0
    return false;
1208
  // Strings need deep comparison.
1209
0
  if (x.isString())
1210
0
    return x.getString()->equals(y.getString());
1211
1212
  // The only remaining case is bigint, which also needs a deep comparison.
1213
0
  return x.isBigInt() && x.getBigInt()->compare(y.getBigInt()) == 0;
1214
0
}
1215
1216
CallResult<HermesValue>
1217
31.1k
addOp_RJS(Runtime &runtime, Handle<> xHandle, Handle<> yHandle) {
1218
31.1k
  auto resX = toPrimitive_RJS(runtime, xHandle, PreferredType::NONE);
1219
31.1k
  if (resX == ExecutionStatus::EXCEPTION) {
1220
0
    return ExecutionStatus::EXCEPTION;
1221
0
  }
1222
31.1k
  auto xPrim = runtime.makeHandle(resX.getValue());
1223
1224
31.1k
  auto resY = toPrimitive_RJS(runtime, yHandle, PreferredType::NONE);
1225
31.1k
  if (resY == ExecutionStatus::EXCEPTION) {
1226
0
    return ExecutionStatus::EXCEPTION;
1227
0
  }
1228
31.1k
  auto yPrim = runtime.makeHandle(resY.getValue());
1229
1230
  // If one of the values is a string, concatenate as strings.
1231
31.1k
  if (xPrim->isString() || yPrim->isString()) {
1232
31.1k
    auto resX = toString_RJS(runtime, xPrim);
1233
31.1k
    if (resX == ExecutionStatus::EXCEPTION) {
1234
0
      return ExecutionStatus::EXCEPTION;
1235
0
    }
1236
31.1k
    auto xStr = runtime.makeHandle(std::move(*resX));
1237
1238
31.1k
    auto resY = toString_RJS(runtime, yPrim);
1239
31.1k
    if (resY == ExecutionStatus::EXCEPTION) {
1240
0
      return ExecutionStatus::EXCEPTION;
1241
0
    }
1242
31.1k
    auto yStr = runtime.makeHandle(std::move(*resY));
1243
1244
31.1k
    return StringPrimitive::concat(runtime, xStr, yStr);
1245
31.1k
  }
1246
1247
  // xPrim and yPrim are primitives; hence, they are already bigints, or they
1248
  // will never be bigints.
1249
0
  if (LLVM_LIKELY(!xPrim->isBigInt())) {
1250
    // xPrim is not a bigint; thus this is Number + Number.
1251
0
    auto res = toNumber_RJS(runtime, xPrim);
1252
0
    if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
1253
0
      return ExecutionStatus::EXCEPTION;
1254
0
    }
1255
0
    const double xNum = res->getNumber();
1256
    // N.B.: toNumber(yPrim) will raise an TypeError if yPrim is bigint, which
1257
    // is the correct exception to be raised when trying to perform
1258
    // Number + BigInt. This avoids the need to check if yPrim is a bigint.
1259
0
    res = toNumber_RJS(runtime, yPrim);
1260
0
    if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
1261
0
      return ExecutionStatus::EXCEPTION;
1262
0
    }
1263
0
    const double yNum = res->getNumber();
1264
0
    return HermesValue::encodeUntrustedNumberValue(xNum + yNum);
1265
0
  }
1266
1267
  // yPrim is a primitive; therefore it is already a BigInt, or it will never be
1268
  // one.
1269
0
  if (!yPrim->isBigInt()) {
1270
0
    return runtime.raiseTypeErrorForValue(
1271
0
        "Cannot convert ", yHandle, " to BigInt");
1272
0
  }
1273
1274
0
  return BigIntPrimitive::add(
1275
0
      runtime,
1276
0
      runtime.makeHandle(xPrim->getBigInt()),
1277
0
      runtime.makeHandle(yPrim->getBigInt()));
1278
0
}
1279
1280
static const size_t MIN_RADIX = 2;
1281
static const size_t MAX_RADIX = 36;
1282
1283
0
static inline char toRadixChar(unsigned x, unsigned radix) {
1284
0
  const char chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1285
0
  static_assert(sizeof(chars) - 1 == MAX_RADIX, "Invalid chars array");
1286
0
  assert(
1287
0
      x < radix && x < std::strlen(chars) &&
1288
0
      "invalid number to radix conversion");
1289
0
  return chars[x];
1290
0
}
1291
1292
/// \return the exponent component of the double \p x.
1293
0
static inline int doubleExponent(double x) {
1294
0
  int e;
1295
0
  std::frexp(x, &e);
1296
0
  return e;
1297
0
}
1298
1299
Handle<StringPrimitive>
1300
0
numberToStringWithRadix(Runtime &runtime, double number, unsigned radix) {
1301
0
  (void)MIN_RADIX;
1302
0
  (void)MAX_RADIX;
1303
0
  assert(MIN_RADIX <= radix && radix <= MAX_RADIX && "Invalid radix");
1304
  // Two parts of the final result: integer part and fractional part.
1305
0
  llvh::SmallString<64> result{};
1306
1307
  // Used to store just the fractional part of the string (not including '.').
1308
0
  llvh::SmallString<32> fStr{};
1309
1310
  // If negative, treat as if positive and add a '-' later.
1311
0
  bool negative = false;
1312
0
  if (number < 0) {
1313
0
    negative = true;
1314
0
    number = -number;
1315
0
  }
1316
1317
  // Split number into integer and fractional parts.
1318
0
  double iPart;
1319
0
  double fPart = std::modf(number, &iPart);
1320
1321
  // If there's a fractional part, convert it and store in fStr.
1322
0
  if (fPart != 0) {
1323
    // Distance to the next double value.
1324
0
    double next =
1325
0
        std::nextafter(number, std::numeric_limits<double>::infinity());
1326
0
    double minDenorm =
1327
0
        std::nextafter(0.0, std::numeric_limits<double>::infinity());
1328
1329
    // Precision of the input (half the distance to the next double).
1330
    // We only compute digits up to that precision.
1331
    // Ensure that delta > 0 by clamping it by the min denormalized positive
1332
    // double number.
1333
0
    double delta = std::max(0.5 * (next - number), minDenorm);
1334
1335
0
    while (fPart > delta) {
1336
      // Multiply by radix to find the next digit.
1337
0
      fPart *= radix;
1338
0
      delta *= radix;
1339
      // Write the next digit.
1340
0
      unsigned digit = static_cast<unsigned>(fPart);
1341
0
      fStr.push_back(toRadixChar(digit, radix));
1342
      // Remove current digit from fPart to prepare for next iteration.
1343
0
      fPart -= digit;
1344
      // Round-to-even.
1345
0
      if (fPart > 0.5 || (fPart == 0.5 && (digit & 1))) {
1346
        // Must round up, necessitating changing written digits.
1347
0
        if (fPart + delta > 1) {
1348
          // Round because printing the next closest double would not give
1349
          // closer results than rounding. The distance between the next
1350
          // double and this one is large enough that at this point, we're
1351
          // doing worse than rounding up if we were to print out the next
1352
          // double precisely.
1353
0
          while (true) {
1354
            // Rounding requires backtracking to fix everything up.
1355
0
            if (fStr.size() == 0) {
1356
              // Rounding failed to stop in the fractional part,
1357
              // so carry over to the integral part.
1358
0
              ++iPart;
1359
0
              break;
1360
0
            }
1361
            // Iterator to the last digit of the string.
1362
0
            char &c = fStr.back();
1363
0
            unsigned digitForC = c <= '9' ? c - '0' : c - 'a' + 10;
1364
0
            if (digitForC + 1 < radix) {
1365
              // Can increment this digit, and we're done.
1366
0
              c = toRadixChar(digitForC + 1, radix);
1367
0
              break;
1368
0
            }
1369
            // We weren't able to increment, so this will be a trailing 0,
1370
            // which we don't want to keep around anyway. So, pop the last
1371
            // digit off the string and continue on.
1372
0
            fStr.pop_back();
1373
0
          }
1374
          // Rounded off the number, done writing the fractional string.
1375
0
          break;
1376
0
        }
1377
0
      }
1378
0
    }
1379
0
  }
1380
1381
  // Now, create the integer part.
1382
0
  if (iPart == 0) {
1383
0
    result.push_back('0');
1384
0
  } else {
1385
    // Write the number backwards, then reverse it. This simplifies the code.
1386
1387
    // Physical mantissa size.
1388
    // Hidden bit is not included because it's not after the decimal point in
1389
    // the binary scientific notation representation of a double.
1390
    // We use this to calculate whether we have the precision required to know
1391
    // what the next digit is going to be.
1392
0
    constexpr const int MANTISSA_SIZE = DBL_MANT_DIG - 1;
1393
1394
    // Handle trailing zeros.
1395
0
    while (doubleExponent(iPart / radix) > MANTISSA_SIZE) {
1396
      // (iPart / radix) doesn't have enough precision to be useful here,
1397
      // because its exponent is larger than the number of bits that the
1398
      // mantissa can encode. So, just put a trailing zero, divide by radix,
1399
      // and move on to the next digit.
1400
0
      result.push_back('0');
1401
0
      iPart /= radix;
1402
0
    }
1403
1404
    // Print the rest of the string when we know we have enough precision to
1405
    // do so.
1406
0
    while (iPart > 0) {
1407
      // Cast digit to int because we know 2 <= digit <= 36.
1408
0
      int digit = static_cast<int>(std::fmod(iPart, radix));
1409
0
      result.push_back(toRadixChar(digit, radix));
1410
0
      iPart = (iPart - digit) / radix;
1411
0
    }
1412
1413
    // Int string was generated in reverse, so flip it.
1414
0
    std::reverse(result.begin(), result.end());
1415
0
  }
1416
1417
  // Concatenate the fractional string on if it exists.
1418
0
  if (!fStr.empty()) {
1419
0
    result += '.';
1420
0
    result += fStr;
1421
0
  }
1422
1423
  // Account for negative numbers.
1424
0
  if (negative) {
1425
0
    result.insert(result.begin(), '-');
1426
0
  }
1427
1428
0
  return runtime.makeHandle<StringPrimitive>(runtime.ignoreAllocationFailure(
1429
0
      StringPrimitive::create(runtime, result)));
1430
0
}
1431
1432
CallResult<PseudoHandle<>>
1433
495
getMethod(Runtime &runtime, Handle<> O, Handle<> key) {
1434
495
  GCScopeMarkerRAII gcScope{runtime};
1435
495
  auto objRes = toObject(runtime, O);
1436
495
  if (LLVM_UNLIKELY(objRes == ExecutionStatus::EXCEPTION)) {
1437
0
    return ExecutionStatus::EXCEPTION;
1438
0
  }
1439
495
  auto obj = runtime.makeHandle<JSObject>(*objRes);
1440
495
  auto funcRes = JSObject::getComputed_RJS(obj, runtime, key);
1441
495
  if (LLVM_UNLIKELY(funcRes == ExecutionStatus::EXCEPTION)) {
1442
0
    return ExecutionStatus::EXCEPTION;
1443
0
  }
1444
495
  if ((*funcRes)->isUndefined() || (*funcRes)->isNull()) {
1445
495
    return PseudoHandle<>::create(HermesValue::encodeUndefinedValue());
1446
495
  }
1447
0
  if (!vmisa<Callable>(funcRes->get())) {
1448
0
    return runtime.raiseTypeError("Could not get callable method from object");
1449
0
  }
1450
0
  return funcRes;
1451
0
}
1452
1453
CallResult<IteratorRecord> getIterator(
1454
    Runtime &runtime,
1455
    Handle<> obj,
1456
2
    llvh::Optional<Handle<Callable>> methodOpt) {
1457
2
  MutableHandle<Callable> method{runtime};
1458
2
  if (LLVM_LIKELY(!methodOpt.hasValue())) {
1459
2
    auto methodRes = getMethod(
1460
2
        runtime,
1461
2
        obj,
1462
2
        runtime.makeHandle(
1463
2
            Predefined::getSymbolID(Predefined::SymbolIterator)));
1464
2
    if (LLVM_UNLIKELY(methodRes == ExecutionStatus::EXCEPTION)) {
1465
0
      return ExecutionStatus::EXCEPTION;
1466
0
    }
1467
2
    if (!vmisa<Callable>(methodRes->getHermesValue())) {
1468
2
      return runtime.raiseTypeError("iterator method is not callable");
1469
2
    }
1470
0
    method = vmcast<Callable>(methodRes->getHermesValue());
1471
0
  } else {
1472
0
    method = **methodOpt;
1473
0
  }
1474
0
  auto iteratorRes = Callable::executeCall0(method, runtime, obj);
1475
0
  if (LLVM_UNLIKELY(iteratorRes == ExecutionStatus::EXCEPTION)) {
1476
0
    return ExecutionStatus::EXCEPTION;
1477
0
  }
1478
0
  if (LLVM_UNLIKELY(!(*iteratorRes)->isObject())) {
1479
0
    return runtime.raiseTypeError("iterator is not an object");
1480
0
  }
1481
0
  auto iterator = runtime.makeHandle<JSObject>(std::move(*iteratorRes));
1482
1483
0
  CallResult<PseudoHandle<>> nextMethodRes = JSObject::getNamed_RJS(
1484
0
      iterator, runtime, Predefined::getSymbolID(Predefined::next));
1485
0
  if (LLVM_UNLIKELY(nextMethodRes == ExecutionStatus::EXCEPTION)) {
1486
0
    return ExecutionStatus::EXCEPTION;
1487
0
  }
1488
1489
  // We perform this check prior to returning, because every function in the JS
1490
  // library which gets an iterator immediately calls the 'next' function.
1491
0
  if (!vmisa<Callable>(nextMethodRes->get())) {
1492
0
    return runtime.raiseTypeError("'next' method on iterator must be callable");
1493
0
  }
1494
1495
0
  auto nextMethod =
1496
0
      Handle<Callable>::vmcast(runtime.makeHandle(std::move(*nextMethodRes)));
1497
1498
0
  return IteratorRecord{iterator, nextMethod};
1499
0
}
1500
1501
CallResult<PseudoHandle<JSObject>> iteratorNext(
1502
    Runtime &runtime,
1503
    const IteratorRecord &iteratorRecord,
1504
0
    llvh::Optional<Handle<>> value) {
1505
0
  GCScopeMarkerRAII marker{runtime};
1506
0
  auto resultRes = value
1507
0
      ? Callable::executeCall1(
1508
0
            iteratorRecord.nextMethod,
1509
0
            runtime,
1510
0
            iteratorRecord.iterator,
1511
0
            value->getHermesValue())
1512
0
      : Callable::executeCall0(
1513
0
            iteratorRecord.nextMethod, runtime, iteratorRecord.iterator);
1514
0
  if (LLVM_UNLIKELY(resultRes == ExecutionStatus::EXCEPTION)) {
1515
0
    return ExecutionStatus::EXCEPTION;
1516
0
  }
1517
0
  if (LLVM_UNLIKELY(!(*resultRes)->isObject())) {
1518
0
    return runtime.raiseTypeError("iterator.next() did not return an object");
1519
0
  }
1520
0
  return PseudoHandle<JSObject>::vmcast(std::move(*resultRes));
1521
0
}
1522
1523
CallResult<PseudoHandle<HermesValue>> iteratorValue(
1524
    Runtime &runtime,
1525
0
    Handle<JSObject> iterResult) {
1526
0
  return JSObject::getNamed_RJS(
1527
0
      iterResult, runtime, Predefined::getSymbolID(Predefined::value));
1528
0
}
1529
1530
CallResult<Handle<JSObject>> iteratorStep(
1531
    Runtime &runtime,
1532
0
    const IteratorRecord &iteratorRecord) {
1533
0
  auto resultRes = iteratorNext(runtime, iteratorRecord);
1534
0
  if (LLVM_UNLIKELY(resultRes == ExecutionStatus::EXCEPTION)) {
1535
0
    return ExecutionStatus::EXCEPTION;
1536
0
  }
1537
0
  Handle<JSObject> result = runtime.makeHandle(std::move(*resultRes));
1538
0
  auto completeRes = JSObject::getNamed_RJS(
1539
0
      result, runtime, Predefined::getSymbolID(Predefined::done));
1540
0
  if (LLVM_UNLIKELY(completeRes == ExecutionStatus::EXCEPTION)) {
1541
0
    return ExecutionStatus::EXCEPTION;
1542
0
  }
1543
0
  if (toBoolean(completeRes->get())) {
1544
0
    return Runtime::makeNullHandle<JSObject>();
1545
0
  }
1546
0
  return result;
1547
0
}
1548
1549
ExecutionStatus iteratorClose(
1550
    Runtime &runtime,
1551
    Handle<JSObject> iterator,
1552
0
    Handle<> completion) {
1553
0
  ExecutionStatus completionStatus = completion->isEmpty()
1554
0
      ? ExecutionStatus::RETURNED
1555
0
      : ExecutionStatus::EXCEPTION;
1556
1557
  // 4. Let innerResult be GetMethod(iterator, "return").
1558
  // Do this lazily: innerResult is only actually used if GetMethod returns
1559
  // a callable which, when called, doesn't throw. Defer storing to innerResult
1560
  // until that point.
1561
0
  auto returnRes = getMethod(
1562
0
      runtime,
1563
0
      iterator,
1564
0
      runtime.makeHandle(Predefined::getSymbolID(Predefined::returnStr)));
1565
1566
0
  MutableHandle<> innerResult{runtime};
1567
0
  if (LLVM_LIKELY(returnRes != ExecutionStatus::EXCEPTION)) {
1568
0
    if (!vmisa<Callable>(returnRes->getHermesValue())) {
1569
0
      runtime.setThrownValue(*completion);
1570
0
      return completionStatus;
1571
0
    }
1572
0
    Handle<Callable> returnFn =
1573
0
        runtime.makeHandle(vmcast<Callable>(returnRes->getHermesValue()));
1574
0
    auto innerResultRes = Callable::executeCall0(returnFn, runtime, iterator);
1575
0
    if (LLVM_UNLIKELY(innerResultRes == ExecutionStatus::EXCEPTION)) {
1576
0
      if (isUncatchableError(runtime.getThrownValue())) {
1577
        // If the call to return threw an uncatchable exception, that overrides
1578
        // the completion, since the point of an uncatchable exception is to
1579
        // prevent more JS from executing.
1580
0
        return ExecutionStatus::EXCEPTION;
1581
0
      }
1582
      // If the error is catchable, suppress it temporarily below in lieu
1583
      // of the returnRes exception by writing to innerResultException.
1584
      // Spec text overwrites the value in `innerResult`.
1585
0
    } else {
1586
0
      innerResult = std::move(*innerResultRes);
1587
0
    }
1588
0
  }
1589
  // Runtime::thrownValue now contains the innerResult's exception if it
1590
  // was thrown.
1591
  // GetMethod error here is deliberately suppressed (no "?" in the spec).
1592
0
  if (completionStatus == ExecutionStatus::EXCEPTION) {
1593
    // 6. If completion.[[Type]] is throw, return Completion(completion).
1594
    // Note: Overrides the innerResult exception.
1595
0
    runtime.setThrownValue(*completion);
1596
0
    return ExecutionStatus::EXCEPTION;
1597
0
  }
1598
0
  if (LLVM_UNLIKELY(!runtime.getThrownValue().isEmpty())) {
1599
    // 7. If innerResult.[[Type]] is throw, return Completion(innerResult).
1600
    // Note: innerResult exception is still in Runtime::thrownValue,
1601
    // so there is no need to set it again.
1602
0
    return ExecutionStatus::EXCEPTION;
1603
0
  }
1604
0
  if (!innerResult->isObject()) {
1605
    // 8. If Type(innerResult.[[Value]]) is not Object,
1606
    //    throw a TypeError exception.
1607
0
    return runtime.raiseTypeError("iterator.return() did not return an object");
1608
0
  }
1609
0
  return ExecutionStatus::RETURNED;
1610
0
}
1611
1612
CallResult<Handle<JSArray>> iterableToArray(
1613
    Runtime &runtime,
1614
0
    Handle<HermesValue> items) {
1615
  // IterableToList: 2a. Let iteratorRecord be ? GetIterator(items, sync).
1616
0
  CallResult<IteratorRecord> iteratorRecordRes = getIterator(runtime, items);
1617
0
  if (LLVM_UNLIKELY(iteratorRecordRes == ExecutionStatus::EXCEPTION)) {
1618
0
    return ExecutionStatus::EXCEPTION;
1619
0
  }
1620
0
  IteratorRecord iteratorRecord = *iteratorRecordRes;
1621
1622
  // CreateArrayFromList: 1. Let array be ! ArrayCreate(0).
1623
0
  auto arrRes = JSArray::create(runtime, 0, 0);
1624
0
  assert(arrRes != ExecutionStatus::EXCEPTION && "could not create array");
1625
0
  Handle<JSArray> array = *arrRes;
1626
  // CreateArrayFromList: 2. Let n be 0.
1627
0
  size_t n = 0;
1628
1629
0
  GCScopeMarkerRAII marker{runtime};
1630
0
  for (;; marker.flush()) {
1631
    // IterableToList: 5.a. Set next to ? IteratorStep(iteratorRecord).
1632
0
    CallResult<Handle<JSObject>> nextRes =
1633
0
        iteratorStep(runtime, iteratorRecord);
1634
0
    if (LLVM_UNLIKELY(nextRes == ExecutionStatus::EXCEPTION)) {
1635
0
      return ExecutionStatus::EXCEPTION;
1636
0
    }
1637
0
    if (!*nextRes) {
1638
0
      break;
1639
0
    }
1640
    // 5.b.i. Let nextValue be ? IteratorValue(next).
1641
0
    CallResult<PseudoHandle<HermesValue>> nextValueRes =
1642
0
        iteratorValue(runtime, *nextRes);
1643
0
    if (LLVM_UNLIKELY(nextValueRes == ExecutionStatus::EXCEPTION)) {
1644
0
      return ExecutionStatus::EXCEPTION;
1645
0
    }
1646
    // CreateArrayFromList: 3.a Perform ! CreateDataPropertyOrThrow(array, !
1647
    // ToString(𝔽(n)), e).
1648
0
    JSArray::setElementAt(
1649
0
        array, runtime, n, runtime.makeHandle(std::move(*nextValueRes)));
1650
    // CreateArrayFromList: 3.b Set n to n + 1.
1651
0
    n++;
1652
0
  }
1653
0
  if (LLVM_UNLIKELY(
1654
0
          JSArray::setLengthProperty(array, runtime, n) ==
1655
0
          ExecutionStatus::EXCEPTION)) {
1656
0
    return ExecutionStatus::EXCEPTION;
1657
0
  }
1658
  // 4. Return array.
1659
0
  return array;
1660
0
}
1661
1662
0
bool isUncatchableError(HermesValue value) {
1663
0
  if (auto *jsError = dyn_vmcast<JSError>(value)) {
1664
0
    return !jsError->catchable();
1665
0
  }
1666
0
  return false;
1667
0
}
1668
1669
Handle<JSObject>
1670
0
createIterResultObject(Runtime &runtime, Handle<> value, bool done) {
1671
0
  auto objHandle = runtime.makeHandle(JSObject::create(runtime));
1672
0
  auto status = JSObject::defineOwnProperty(
1673
0
      objHandle,
1674
0
      runtime,
1675
0
      Predefined::getSymbolID(Predefined::value),
1676
0
      DefinePropertyFlags::getDefaultNewPropertyFlags(),
1677
0
      value);
1678
0
  (void)status;
1679
0
  assert(
1680
0
      status != ExecutionStatus::EXCEPTION && *status &&
1681
0
      "put own value property cannot fail");
1682
0
  status = JSObject::defineOwnProperty(
1683
0
      objHandle,
1684
0
      runtime,
1685
0
      Predefined::getSymbolID(Predefined::done),
1686
0
      DefinePropertyFlags::getDefaultNewPropertyFlags(),
1687
0
      Runtime::getBoolValue(done));
1688
0
  assert(
1689
0
      status != ExecutionStatus::EXCEPTION && *status &&
1690
0
      "put own value property cannot fail");
1691
0
  return objHandle;
1692
0
}
1693
1694
CallResult<Handle<Callable>> speciesConstructor(
1695
    Handle<JSObject> O,
1696
    Runtime &runtime,
1697
0
    Handle<Callable> defaultConstructor) {
1698
  // construct from the "constructor" property in self if that is defined, else
1699
  // use the default one.
1700
0
  auto res = JSObject::getNamed_RJS(
1701
0
      O, runtime, Predefined::getSymbolID(Predefined::constructor));
1702
0
  if (res == ExecutionStatus::EXCEPTION) {
1703
0
    return ExecutionStatus::EXCEPTION;
1704
0
  }
1705
0
  PseudoHandle<> cons = std::move(*res);
1706
0
  if (cons->isUndefined()) {
1707
0
    return defaultConstructor;
1708
0
  }
1709
0
  if (!cons->isObject()) {
1710
0
    return runtime.raiseTypeError(
1711
0
        "Constructor must be an object if it is not undefined");
1712
0
  }
1713
  // There is no @@species (no Symbols yet), so we'll assume that there was
1714
  // no other constructor specified.
1715
0
  return defaultConstructor;
1716
0
}
1717
1718
0
CallResult<bool> isConstructor(Runtime &runtime, HermesValue value) {
1719
0
  return isConstructor(runtime, dyn_vmcast<Callable>(value));
1720
0
}
1721
1722
0
CallResult<bool> isConstructor(Runtime &runtime, Callable *callable) {
1723
  // This is not a complete definition, since ES6 and later define member
1724
  // functions of objects to not be constructors; however, Hermes does not have
1725
  // ES6 classes implemented yet, so we cannot check for that case.
1726
0
  if (!callable) {
1727
0
    return false;
1728
0
  }
1729
1730
  // We traverse the BoundFunction target chain to find the eventual target.
1731
0
  while (BoundFunction *b = dyn_vmcast<BoundFunction>(callable)) {
1732
0
    callable = b->getTarget(runtime);
1733
0
  }
1734
1735
  // If it is a bytecode function, check the flags.
1736
0
  if (auto *func = dyn_vmcast<JSFunction>(callable)) {
1737
0
    auto *cb = func->getCodeBlock(runtime);
1738
    // Even though it doesn't make sense logically, we need to compile the
1739
    // function in order to access it flags.
1740
0
    if (LLVM_UNLIKELY(cb->lazyCompile(runtime) == ExecutionStatus::EXCEPTION)) {
1741
0
      return ExecutionStatus::EXCEPTION;
1742
0
    }
1743
0
    return !func->getCodeBlock(runtime)->getHeaderFlags().isCallProhibited(
1744
0
        true);
1745
0
  }
1746
1747
  // We check for NativeFunction since those are defined to not be
1748
  // constructible, with the exception of NativeConstructor.
1749
0
  if (!vmisa<NativeFunction>(callable) || vmisa<NativeConstructor>(callable)) {
1750
0
    return true;
1751
0
  }
1752
1753
  // JSCallableProxy is a NativeFunction, but may or may not be a
1754
  // constructor, so we ask it.
1755
0
  if (auto *cproxy = dyn_vmcast<JSCallableProxy>(callable)) {
1756
0
    return cproxy->isConstructor(runtime);
1757
0
  }
1758
1759
0
  return false;
1760
0
}
1761
1762
CallResult<bool>
1763
0
ordinaryHasInstance(Runtime &runtime, Handle<> constructor, Handle<> object) {
1764
  // 1. If IsCallable(C) is false, return false.
1765
0
  if (!vmisa<Callable>(*constructor)) {
1766
0
    return false;
1767
0
  }
1768
1769
0
  Callable *ctor = vmcast<Callable>(*constructor);
1770
1771
0
  BoundFunction *bound;
1772
  // 2. If C has a [[BoundTargetFunction]] internal slot, then
1773
0
  while (LLVM_UNLIKELY(bound = dyn_vmcast<BoundFunction>(ctor))) {
1774
    // 2a. Let BC be the value of C’s [[BoundTargetFunction]] internal slot.
1775
    // 2b. Return InstanceofOperator(O,BC) (see 12.9.4).
1776
    // Note that we can do this with the loop instead,
1777
    // because bound->getTarget() must be a Callable, and Callables cannot
1778
    // redefine @@hasInstance (non-configurable).
1779
    // Callables call this function directly from their @@hasInstance
1780
    // function.
1781
0
    ctor = bound->getTarget(runtime);
1782
0
  }
1783
1784
  // At this point 'ctor' is the actual function with a prototype.
1785
0
  assert(ctor != nullptr && "ctor must not be null");
1786
1787
  // 3. If Type(O) is not Object, return false.
1788
0
  if (LLVM_UNLIKELY(!object->isObject())) {
1789
0
    return false;
1790
0
  }
1791
1792
  // 4. Let P be Get(C, "prototype").
1793
0
  auto propRes = JSObject::getNamed_RJS(
1794
0
      runtime.makeHandle(ctor),
1795
0
      runtime,
1796
0
      Predefined::getSymbolID(Predefined::prototype));
1797
0
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1798
0
    return ExecutionStatus::EXCEPTION;
1799
0
  }
1800
1801
  // 5. If Type(P) is not Object, throw a TypeError exception.
1802
0
  Handle<JSObject> ctorPrototype = runtime.makeHandle(
1803
0
      PseudoHandle<JSObject>::dyn_vmcast(std::move(*propRes)));
1804
0
  if (LLVM_UNLIKELY(!ctorPrototype)) {
1805
0
    return runtime.raiseTypeError(
1806
0
        "function's '.prototype' is not an object in 'instanceof'");
1807
0
  }
1808
1809
  // 6.1.7.3 Invariants of the Essential Internal Methods notes that
1810
  // detection of infinite prototype chains is not enforceable as an
1811
  // invariant if exotic objects exist in the chain.  Most of the
1812
  // time, ScopedNativeDepthTracker will detect this. Here, we need to
1813
  // check that we're not repeating forever.  Since ordinary object
1814
  // chains are verified at the time the parent is set, we count Proxy
1815
  // objects.  Thus, any length chain of ordinary objects is ok.
1816
0
  constexpr unsigned int kMaxProxyCount = 1024;
1817
0
  unsigned int proxyCount = 0;
1818
0
  MutableHandle<JSObject> head{runtime, vmcast<JSObject>(object.get())};
1819
0
  GCScopeMarkerRAII gcScope{runtime};
1820
  // 6. Repeat
1821
0
  while (true) {
1822
    // 6a. Let O be O.[[GetPrototypeOf]]().
1823
0
    CallResult<PseudoHandle<JSObject>> parentRes =
1824
0
        JSObject::getPrototypeOf(head, runtime);
1825
0
    if (LLVM_UNLIKELY(parentRes == ExecutionStatus::EXCEPTION)) {
1826
0
      return ExecutionStatus::EXCEPTION;
1827
0
    }
1828
    // 6b. If O is null, return false.
1829
0
    if (!*parentRes) {
1830
0
      return false;
1831
0
    }
1832
    // 6c. If SameValue(P, O) is true, return true.
1833
0
    if (parentRes->get() == ctorPrototype.get()) {
1834
0
      return true;
1835
0
    }
1836
0
    if (head->isProxyObject()) {
1837
0
      ++proxyCount;
1838
0
      if (proxyCount > kMaxProxyCount) {
1839
0
        return runtime.raiseRangeError(
1840
0
            "Maximum prototype chain length exceeded");
1841
0
      }
1842
0
    }
1843
0
    head = parentRes->get();
1844
0
    gcScope.flush();
1845
0
  }
1846
0
}
1847
1848
CallResult<bool> instanceOfOperator_RJS(
1849
    Runtime &runtime,
1850
    Handle<> object,
1851
0
    Handle<> constructor) {
1852
  // 1. If Type(C) is not Object, throw a TypeError exception.
1853
0
  if (LLVM_UNLIKELY(!constructor->isObject())) {
1854
0
    return runtime.raiseTypeError(
1855
0
        "right operand of 'instanceof' is not an object");
1856
0
  }
1857
1858
  // Fast path: Function.prototype[Symbol.hasInstance] is non-configurable
1859
  // and non-writable (ES6.0 19.2.3.6), so we directly run its behavior here.
1860
  // Simply call through to ordinaryHasInstance.
1861
0
  if (vmisa<JSFunction>(*constructor)) {
1862
0
    return ordinaryHasInstance(runtime, constructor, object);
1863
0
  }
1864
1865
  // 2. Let instOfHandler be GetMethod(C,@@hasInstance).
1866
0
  CallResult<PseudoHandle<>> instOfHandlerRes = JSObject::getNamed_RJS(
1867
0
      Handle<JSObject>::vmcast(constructor),
1868
0
      runtime,
1869
0
      Predefined::getSymbolID(Predefined::SymbolHasInstance));
1870
0
  if (LLVM_UNLIKELY(instOfHandlerRes == ExecutionStatus::EXCEPTION)) {
1871
0
    return ExecutionStatus::EXCEPTION;
1872
0
  }
1873
0
  auto instOfHandler = runtime.makeHandle(std::move(*instOfHandlerRes));
1874
1875
  // 4. If instOfHandler is not undefined, then
1876
0
  if (!instOfHandler->isUndefined()) {
1877
    // 5. Return ToBoolean(Call(instOfHandler, C, «O»)).
1878
0
    if (!vmisa<Callable>(*instOfHandler)) {
1879
0
      return runtime.raiseTypeError("instanceof handler must be callable");
1880
0
    }
1881
0
    auto callRes = Callable::executeCall1(
1882
0
        Handle<Callable>::vmcast(instOfHandler), runtime, constructor, *object);
1883
0
    if (LLVM_UNLIKELY(callRes == ExecutionStatus::EXCEPTION)) {
1884
0
      return ExecutionStatus::EXCEPTION;
1885
0
    }
1886
0
    return toBoolean(callRes->get());
1887
0
  }
1888
1889
  // 6. If IsCallable(C) is false, throw a TypeError exception.
1890
0
  if (!vmisa<Callable>(*constructor)) {
1891
0
    return runtime.raiseTypeError(
1892
0
        "right operand of 'instanceof' is not callable");
1893
0
  }
1894
1895
  // 7. Return OrdinaryHasInstance(C, O).
1896
0
  return ordinaryHasInstance(runtime, constructor, object);
1897
0
}
1898
1899
/// ES6.0 7.2.8
1900
/// Returns true if the object is a JSRegExp or has a Symbol.match property that
1901
/// evaluates to true.
1902
76
CallResult<bool> isRegExp(Runtime &runtime, Handle<> arg) {
1903
  // 1. If Type(argument) is not Object, return false.
1904
76
  if (!arg->isObject()) {
1905
0
    return false;
1906
0
  }
1907
76
  Handle<JSObject> obj = Handle<JSObject>::vmcast(arg);
1908
  // 2. Let isRegExp be Get(argument, @@match).
1909
76
  auto propRes = JSObject::getNamed_RJS(
1910
76
      obj, runtime, Predefined::getSymbolID(Predefined::SymbolMatch));
1911
  // 3. ReturnIfAbrupt(isRegExp).
1912
76
  if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
1913
0
    return ExecutionStatus::EXCEPTION;
1914
0
  }
1915
  // 4. If isRegExp is not undefined, return ToBoolean(isRegExp).
1916
76
  if (!(*propRes)->isUndefined()) {
1917
0
    return toBoolean(propRes->get());
1918
0
  }
1919
  // 5. If argument has a [[RegExpMatcher]] internal slot, return true.
1920
  // 6. Return false.
1921
76
  return vmisa<JSRegExp>(arg.get());
1922
76
}
1923
1924
CallResult<Handle<StringPrimitive>> symbolDescriptiveString(
1925
    Runtime &runtime,
1926
0
    Handle<SymbolID> sym) {
1927
  // 1. Assert: Type(sym) is Symbol.
1928
  // 2. Let desc be sym's [[Description]] value.
1929
  // 3. If desc is undefined, set desc to the empty string.
1930
  // 4. Assert: Type(desc) is String.
1931
0
  auto desc = runtime.makeHandle<StringPrimitive>(
1932
0
      runtime.getStringPrimFromSymbolID(*sym));
1933
0
  SafeUInt32 descLen(desc->getStringLength());
1934
0
  descLen.add(8);
1935
1936
  // 5. Return the string-concatenation of "Symbol(", desc, and ")".
1937
0
  auto builder = StringBuilder::createStringBuilder(runtime, descLen);
1938
0
  if (LLVM_UNLIKELY(builder == ExecutionStatus::EXCEPTION)) {
1939
0
    return ExecutionStatus::EXCEPTION;
1940
0
  }
1941
0
  builder->appendASCIIRef({"Symbol(", 7});
1942
0
  builder->appendStringPrim(desc);
1943
0
  builder->appendCharacter(')');
1944
1945
0
  return builder->getStringPrimitive();
1946
0
}
1947
1948
0
CallResult<bool> isArray(Runtime &runtime, JSObject *obj) {
1949
0
  if (!obj) {
1950
0
    return false;
1951
0
  }
1952
0
  while (true) {
1953
0
    if (vmisa<JSArray>(obj)) {
1954
0
      return true;
1955
0
    }
1956
0
    if (LLVM_LIKELY(!obj->isProxyObject())) {
1957
0
      return false;
1958
0
    }
1959
0
    if (JSProxy::isRevoked(obj, runtime)) {
1960
0
      return runtime.raiseTypeError("Proxy has been revoked");
1961
0
    }
1962
0
    obj = JSProxy::getTarget(obj, runtime).get();
1963
0
    assert(obj && "target of non-revoked Proxy is null");
1964
0
  }
1965
0
}
1966
1967
0
CallResult<bool> isConcatSpreadable(Runtime &runtime, Handle<> value) {
1968
0
  auto O = Handle<JSObject>::dyn_vmcast(value);
1969
0
  if (!O) {
1970
0
    return false;
1971
0
  }
1972
1973
0
  CallResult<PseudoHandle<>> spreadable = JSObject::getNamed_RJS(
1974
0
      O,
1975
0
      runtime,
1976
0
      Predefined::getSymbolID(Predefined::SymbolIsConcatSpreadable));
1977
0
  if (LLVM_UNLIKELY(spreadable == ExecutionStatus::EXCEPTION)) {
1978
0
    return ExecutionStatus::EXCEPTION;
1979
0
  }
1980
1981
0
  if (!(*spreadable)->isUndefined()) {
1982
0
    return toBoolean(spreadable->get());
1983
0
  }
1984
1985
0
  return isArray(runtime, *O);
1986
0
}
1987
1988
ExecutionStatus toPropertyDescriptor(
1989
    Handle<> obj,
1990
    Runtime &runtime,
1991
    DefinePropertyFlags &flags,
1992
0
    MutableHandle<> &valueOrAccessor) {
1993
0
  GCScopeMarkerRAII gcMarker{runtime};
1994
1995
  // Verify that the attributes argument is also an object.
1996
0
  auto attributes = Handle<JSObject>::dyn_vmcast(obj);
1997
0
  if (!attributes) {
1998
0
    return runtime.raiseTypeError(
1999
0
        "Object.defineProperty() Attributes argument is not an object");
2000
0
  }
2001
2002
0
  NamedPropertyDescriptor desc;
2003
2004
  // Get enumerable property of the attributes.
2005
0
  if (JSObject::getNamedDescriptorPredefined(
2006
0
          attributes, runtime, Predefined::enumerable, desc)) {
2007
0
    auto propRes = JSObject::getNamed_RJS(
2008
0
        attributes,
2009
0
        runtime,
2010
0
        Predefined::getSymbolID(Predefined::enumerable),
2011
0
        PropOpFlags().plusThrowOnError());
2012
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2013
0
      return ExecutionStatus::EXCEPTION;
2014
0
    }
2015
0
    flags.enumerable = toBoolean(propRes->get());
2016
0
    flags.setEnumerable = true;
2017
0
  }
2018
2019
  // Get configurable property of the attributes.
2020
0
  if (JSObject::getNamedDescriptorPredefined(
2021
0
          attributes, runtime, Predefined::configurable, desc)) {
2022
0
    auto propRes = JSObject::getNamed_RJS(
2023
0
        attributes,
2024
0
        runtime,
2025
0
        Predefined::getSymbolID(Predefined::configurable),
2026
0
        PropOpFlags().plusThrowOnError());
2027
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2028
0
      return ExecutionStatus::EXCEPTION;
2029
0
    }
2030
0
    flags.configurable = toBoolean(propRes->get());
2031
0
    flags.setConfigurable = true;
2032
0
  }
2033
2034
  // Get value property of the attributes.
2035
0
  if (JSObject::getNamedDescriptorPredefined(
2036
0
          attributes, runtime, Predefined::value, desc)) {
2037
0
    auto propRes = JSObject::getNamed_RJS(
2038
0
        attributes,
2039
0
        runtime,
2040
0
        Predefined::getSymbolID(Predefined::value),
2041
0
        PropOpFlags().plusThrowOnError());
2042
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2043
0
      return ExecutionStatus::EXCEPTION;
2044
0
    }
2045
0
    valueOrAccessor = std::move(*propRes);
2046
0
    flags.setValue = true;
2047
0
  }
2048
2049
  // Get writable property of the attributes.
2050
0
  if (JSObject::getNamedDescriptorPredefined(
2051
0
          attributes, runtime, Predefined::writable, desc)) {
2052
0
    auto propRes = JSObject::getNamed_RJS(
2053
0
        attributes,
2054
0
        runtime,
2055
0
        Predefined::getSymbolID(Predefined::writable),
2056
0
        PropOpFlags().plusThrowOnError());
2057
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2058
0
      return ExecutionStatus::EXCEPTION;
2059
0
    }
2060
0
    flags.writable = toBoolean(propRes->get());
2061
0
    flags.setWritable = true;
2062
0
  }
2063
2064
  // Get getter property of the attributes.
2065
0
  MutableHandle<Callable> getterPtr{runtime};
2066
0
  if (JSObject::getNamedDescriptorPredefined(
2067
0
          attributes, runtime, Predefined::get, desc)) {
2068
0
    auto propRes = JSObject::getNamed_RJS(
2069
0
        attributes,
2070
0
        runtime,
2071
0
        Predefined::getSymbolID(Predefined::get),
2072
0
        PropOpFlags().plusThrowOnError());
2073
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2074
0
      return ExecutionStatus::EXCEPTION;
2075
0
    }
2076
0
    flags.setGetter = true;
2077
0
    PseudoHandle<> getter = std::move(*propRes);
2078
0
    if (LLVM_LIKELY(!getter->isUndefined())) {
2079
0
      getterPtr = dyn_vmcast<Callable>(getter.get());
2080
0
      if (LLVM_UNLIKELY(!getterPtr)) {
2081
0
        return runtime.raiseTypeError(
2082
0
            "Invalid property descriptor. Getter must be a function.");
2083
0
      }
2084
0
    }
2085
0
  }
2086
2087
  // Get setter property of the attributes.
2088
0
  MutableHandle<Callable> setterPtr{runtime};
2089
0
  if (JSObject::getNamedDescriptorPredefined(
2090
0
          attributes, runtime, Predefined::set, desc)) {
2091
0
    auto propRes = JSObject::getNamed_RJS(
2092
0
        attributes,
2093
0
        runtime,
2094
0
        Predefined::getSymbolID(Predefined::set),
2095
0
        PropOpFlags().plusThrowOnError());
2096
0
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
2097
0
      return ExecutionStatus::EXCEPTION;
2098
0
    }
2099
0
    flags.setSetter = true;
2100
0
    PseudoHandle<> setter = std::move(*propRes);
2101
0
    if (LLVM_LIKELY(!setter->isUndefined())) {
2102
0
      setterPtr = PseudoHandle<Callable>::dyn_vmcast(std::move(setter));
2103
0
      if (LLVM_UNLIKELY(!setterPtr)) {
2104
0
        return runtime.raiseTypeError(
2105
0
            "Invalid property descriptor. Setter must be a function.");
2106
0
      }
2107
0
    }
2108
0
  }
2109
2110
  // Construct property accessor if getter/setter is set.
2111
0
  if (flags.setSetter || flags.setGetter) {
2112
0
    if (flags.setValue) {
2113
0
      return runtime.raiseTypeError(
2114
0
          "Invalid property descriptor. Can't set both accessor and value.");
2115
0
    }
2116
0
    if (flags.setWritable) {
2117
0
      return runtime.raiseTypeError(
2118
0
          "Invalid property descriptor. Can't set both accessor and writable.");
2119
0
    }
2120
0
    valueOrAccessor = PropertyAccessor::create(runtime, getterPtr, setterPtr);
2121
0
  }
2122
2123
0
  return ExecutionStatus::RETURNED;
2124
0
}
2125
2126
CallResult<HermesValue> objectFromPropertyDescriptor(
2127
    Runtime &runtime,
2128
    ComputedPropertyDescriptor desc,
2129
0
    Handle<> valueOrAccessor) {
2130
0
  Handle<JSObject> obj = runtime.makeHandle(JSObject::create(runtime));
2131
2132
0
  DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
2133
2134
0
  if (!desc.flags.accessor) {
2135
    // Data Descriptor
2136
0
    auto result = JSObject::defineOwnProperty(
2137
0
        obj,
2138
0
        runtime,
2139
0
        Predefined::getSymbolID(Predefined::value),
2140
0
        dpf,
2141
0
        valueOrAccessor,
2142
0
        PropOpFlags().plusThrowOnError());
2143
0
    assert(
2144
0
        result != ExecutionStatus::EXCEPTION &&
2145
0
        "defineOwnProperty() failed on a new object");
2146
0
    if (result == ExecutionStatus::EXCEPTION) {
2147
0
      return ExecutionStatus::EXCEPTION;
2148
0
    }
2149
2150
0
    result = JSObject::defineOwnProperty(
2151
0
        obj,
2152
0
        runtime,
2153
0
        Predefined::getSymbolID(Predefined::writable),
2154
0
        dpf,
2155
0
        Runtime::getBoolValue(desc.flags.writable),
2156
0
        PropOpFlags().plusThrowOnError());
2157
0
    assert(
2158
0
        result != ExecutionStatus::EXCEPTION &&
2159
0
        "defineOwnProperty() failed on a new object");
2160
0
    if (result == ExecutionStatus::EXCEPTION) {
2161
0
      return ExecutionStatus::EXCEPTION;
2162
0
    }
2163
0
  } else {
2164
    // Accessor
2165
0
    auto *accessor = vmcast<PropertyAccessor>(valueOrAccessor.get());
2166
2167
0
    auto getter = runtime.makeHandle(
2168
0
        accessor->getter ? HermesValue::encodeObjectValue(
2169
0
                               accessor->getter.getNonNull(runtime))
2170
0
                         : HermesValue::encodeUndefinedValue());
2171
2172
0
    auto setter = runtime.makeHandle(
2173
0
        accessor->setter ? HermesValue::encodeObjectValue(
2174
0
                               accessor->setter.getNonNull(runtime))
2175
0
                         : HermesValue::encodeUndefinedValue());
2176
2177
0
    auto result = JSObject::defineOwnProperty(
2178
0
        obj,
2179
0
        runtime,
2180
0
        Predefined::getSymbolID(Predefined::get),
2181
0
        dpf,
2182
0
        getter,
2183
0
        PropOpFlags().plusThrowOnError());
2184
0
    assert(
2185
0
        result != ExecutionStatus::EXCEPTION &&
2186
0
        "defineOwnProperty() failed on a new object");
2187
0
    if (result == ExecutionStatus::EXCEPTION) {
2188
0
      return ExecutionStatus::EXCEPTION;
2189
0
    }
2190
2191
0
    result = JSObject::defineOwnProperty(
2192
0
        obj,
2193
0
        runtime,
2194
0
        Predefined::getSymbolID(Predefined::set),
2195
0
        dpf,
2196
0
        setter,
2197
0
        PropOpFlags().plusThrowOnError());
2198
0
    assert(
2199
0
        result != ExecutionStatus::EXCEPTION &&
2200
0
        "defineOwnProperty() failed on a new object");
2201
0
    if (result == ExecutionStatus::EXCEPTION) {
2202
0
      return ExecutionStatus::EXCEPTION;
2203
0
    }
2204
0
  }
2205
2206
0
  auto result = JSObject::defineOwnProperty(
2207
0
      obj,
2208
0
      runtime,
2209
0
      Predefined::getSymbolID(Predefined::enumerable),
2210
0
      dpf,
2211
0
      Runtime::getBoolValue(desc.flags.enumerable),
2212
0
      PropOpFlags().plusThrowOnError());
2213
0
  assert(
2214
0
      result != ExecutionStatus::EXCEPTION &&
2215
0
      "defineOwnProperty() failed on a new object");
2216
0
  if (result == ExecutionStatus::EXCEPTION) {
2217
0
    return ExecutionStatus::EXCEPTION;
2218
0
  }
2219
2220
0
  result = JSObject::defineOwnProperty(
2221
0
      obj,
2222
0
      runtime,
2223
0
      Predefined::getSymbolID(Predefined::configurable),
2224
0
      dpf,
2225
0
      Runtime::getBoolValue(desc.flags.configurable),
2226
0
      PropOpFlags().plusThrowOnError());
2227
0
  assert(
2228
0
      result != ExecutionStatus::EXCEPTION &&
2229
0
      "defineOwnProperty() failed on a new object");
2230
0
  if (result == ExecutionStatus::EXCEPTION) {
2231
0
    return ExecutionStatus::EXCEPTION;
2232
0
  }
2233
2234
0
  return obj.getHermesValue();
2235
0
}
2236
2237
0
CallResult<HermesValue> numberToBigInt(Runtime &runtime, double number) {
2238
0
  if (!isIntegralNumber(number)) {
2239
0
    return runtime.raiseRangeError("number is not integral");
2240
0
  }
2241
2242
0
  return BigIntPrimitive::fromDouble(runtime, number);
2243
0
}
2244
2245
0
bool isIntegralNumber(double number) {
2246
  // 1. if Type(argument) is not Number, return false
2247
  // it is a number
2248
2249
  // 2. if argument is NaN, +inf, -inf, return false
2250
0
  if (std::isnan(number) || number == std::numeric_limits<double>::infinity() ||
2251
0
      number == -std::numeric_limits<double>::infinity()) {
2252
0
    return false;
2253
0
  }
2254
2255
  // 3. if floor(abs(R(argument))) != abs(R(argument)) return false
2256
0
  if (std::floor(std::abs(number)) != std::abs(number)) {
2257
0
    return false;
2258
0
  }
2259
2260
  // 4. return true
2261
0
  return true;
2262
0
}
2263
2264
0
CallResult<HermesValue> toBigInt_RJS(Runtime &runtime, Handle<> value) {
2265
0
  auto prim = toPrimitive_RJS(runtime, value, PreferredType::NUMBER);
2266
0
  if (LLVM_UNLIKELY(prim == ExecutionStatus::EXCEPTION)) {
2267
0
    return ExecutionStatus::EXCEPTION;
2268
0
  }
2269
2270
0
  switch (prim->getETag()) {
2271
0
    default:
2272
0
      break;
2273
0
    case HermesValue::ETag::Undefined:
2274
0
      return runtime.raiseTypeError("invalid argument to BigInt()");
2275
0
    case HermesValue::ETag::Null:
2276
0
      return runtime.raiseTypeError("invalid argument to BigInt()");
2277
0
    case HermesValue::ETag::Bool:
2278
0
      return BigIntPrimitive::fromSigned(runtime, prim->getBool() ? 1 : 0);
2279
0
    case HermesValue::ETag::BigInt1:
2280
0
    case HermesValue::ETag::BigInt2:
2281
0
      return *prim;
2282
0
    case HermesValue::ETag::Str1:
2283
0
    case HermesValue::ETag::Str2: {
2284
0
      auto n = stringToBigInt(runtime, runtime.makeHandle(*prim));
2285
0
      if (LLVM_UNLIKELY(n == ExecutionStatus::EXCEPTION)) {
2286
0
        return ExecutionStatus::EXCEPTION;
2287
0
      }
2288
0
      if (n->isUndefined()) {
2289
0
        return runtime.raiseSyntaxError("can't convert string to bigint");
2290
0
      }
2291
0
      return *n;
2292
0
    }
2293
0
    case HermesValue::ETag::Symbol:
2294
0
      return runtime.raiseTypeError("invalid argument to BigInt()");
2295
0
  }
2296
2297
0
  return runtime.raiseTypeError("invalid argument to BigInt()");
2298
0
}
2299
2300
0
CallResult<HermesValue> stringToBigInt(Runtime &runtime, Handle<> value) {
2301
0
  if (value->isString()) {
2302
0
    auto str = value->getString();
2303
2304
0
    std::string outError;
2305
0
    auto parsedBigInt = str->isASCII()
2306
0
        ? bigint::ParsedBigInt::parsedBigIntFromStringIntegerLiteral(
2307
0
              str->getStringRef<char>(), &outError)
2308
0
        : bigint::ParsedBigInt::parsedBigIntFromStringIntegerLiteral(
2309
0
              str->getStringRef<char16_t>(), &outError);
2310
0
    if (!parsedBigInt) {
2311
0
      return HermesValue::encodeUndefinedValue();
2312
0
    }
2313
2314
0
    return BigIntPrimitive::fromBytes(runtime, parsedBigInt->getBytes());
2315
0
  }
2316
2317
0
  return runtime.raiseTypeError("Invalid argument to stringToBigInt");
2318
0
}
2319
2320
0
CallResult<HermesValue> thisBigIntValue(Runtime &runtime, Handle<> value) {
2321
0
  if (value->isBigInt())
2322
0
    return *value;
2323
0
  if (auto *jsBigInt = dyn_vmcast<JSBigInt>(*value))
2324
0
    return HermesValue::encodeBigIntValue(
2325
0
        JSBigInt::getPrimitiveBigInt(jsBigInt, runtime));
2326
0
  return runtime.raiseTypeError("value is not a bigint");
2327
0
}
2328
2329
0
bool hasRestrictedGlobalProperty(Runtime &runtime, SymbolID N) {
2330
0
  Handle<JSObject> globalObject = runtime.getGlobal();
2331
2332
  // 1. Let ObjRec be envRec.[[ObjectRecord]].
2333
  // 2. Let globalObject be ObjRec.[[BindingObject]].
2334
2335
  // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
2336
0
  NamedPropertyDescriptor desc;
2337
0
  JSObject *existingProp =
2338
0
      JSObject::getNamedDescriptorUnsafe(globalObject, runtime, N, desc);
2339
2340
  // 4. If existingProp is undefined, return false.
2341
0
  if (!existingProp) {
2342
0
    return false;
2343
0
  }
2344
2345
  // 5. If existingProp.[[Configurable]] is true, return false.
2346
  // 6. Return true.
2347
0
  return !desc.flags.configurable;
2348
0
}
2349
} // namespace vm
2350
} // namespace hermes