Coverage Report

Created: 2025-01-28 06:38

/src/hermes/lib/VM/BigIntPrimitive.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/BigIntPrimitive.h"
9
10
#include "hermes/VM/StringPrimitive.h"
11
12
#include "llvh/ADT/APInt.h"
13
#include "llvh/Support/MathExtras.h"
14
15
#include <cstdlib>
16
#include <limits>
17
#pragma GCC diagnostic push
18
19
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
20
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
21
#endif
22
namespace hermes {
23
namespace vm {
24
25
static_assert(
26
    std::is_same<BigIntPrimitive::DigitType, llvh::APInt::WordType>::value,
27
    "BigIntPrimitive digit must match APInt::WordType");
28
29
const VTable BigIntPrimitive::vt{CellKind::BigIntPrimitiveKind, 0};
30
31
1
void BigIntPrimitiveBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
32
1
  mb.setVTable(&BigIntPrimitive::vt);
33
1
}
34
35
781k
uint32_t BigIntPrimitive::calcCellSizeInBytes(uint32_t numDigits) {
36
781k
  return sizeof(BigIntPrimitive) + numDigits * DigitSizeInBytes;
37
781k
}
38
39
390k
BigIntPrimitive::BigIntPrimitive(uint32_t numDigits) : numDigits(numDigits) {
40
390k
  assert(
41
390k
      (calcCellSizeInBytes(numDigits) - numTrailingBytes() ==
42
390k
       sizeof(BigIntPrimitive)) &&
43
390k
      "cell must fit BigIntPrimitive + Digits exactly");
44
390k
}
45
46
CallResult<HermesValue> BigIntPrimitive::fromDouble(
47
    Runtime &runtime,
48
0
    double value) {
49
  // If our double is perfectly representing a 32-bit int, we can
50
  // just convert directly rather than creating then shrinking an
51
  // array.
52
0
  if (value < std::numeric_limits<int32_t>::max() &&
53
0
      value > std::numeric_limits<int32_t>::min()) {
54
0
    int32_t valueAsInt = static_cast<int32_t>(value);
55
0
    if (static_cast<double>(valueAsInt) == value) {
56
0
      return fromSigned(runtime, valueAsInt);
57
0
    }
58
0
  }
59
60
0
  const uint32_t numDigits = bigint::fromDoubleResultSize(value);
61
62
0
  auto u =
63
0
      BigIntPrimitive::createUninitializedWithNumDigits(runtime, numDigits);
64
65
0
  if (LLVM_UNLIKELY(u == ExecutionStatus::EXCEPTION)) {
66
0
    return ExecutionStatus::EXCEPTION;
67
0
  }
68
69
0
  auto res = bigint::fromDouble(u->getMutableRef(runtime), value);
70
0
  if (LLVM_UNLIKELY(res != bigint::OperationStatus::RETURNED)) {
71
0
    return raiseOnError(runtime, res);
72
0
  }
73
74
0
  return HermesValue::encodeBigIntValue(u->getBigIntPrimitive());
75
0
}
76
77
CallResult<HermesValue> BigIntPrimitive::toString(
78
    Runtime &runtime,
79
    PseudoHandle<BigIntPrimitive> self,
80
781k
    uint8_t radix) {
81
781k
  std::string result = bigint::toString(self->getImmutableRef(runtime), radix);
82
781k
  return StringPrimitive::createEfficient(
83
781k
      runtime, createASCIIRef(result.c_str()));
84
781k
}
85
86
template <auto Op>
87
0
static auto makeTruncAdapter(uint64_t n) {
88
0
  return [n](bigint::MutableBigIntRef dst, bigint::ImmutableBigIntRef src) {
89
0
    return (*Op)(dst, n, src);
90
0
  };
Unexecuted instantiation: BigIntPrimitive.cpp:_ZZN6hermes2vmL16makeTruncAdapterITnDaXadL_ZNS_6bigint6asIntNENS2_16MutableBigIntRefEmNS2_18ImmutableBigIntRefEEEEEDamENKUlS3_S4_E_clES3_S4_
Unexecuted instantiation: BigIntPrimitive.cpp:_ZZN6hermes2vmL16makeTruncAdapterITnDaXadL_ZNS_6bigint7asUintNENS2_16MutableBigIntRefEmNS2_18ImmutableBigIntRefEEEEEDamENKUlS3_S4_E_clES3_S4_
91
0
}
Unexecuted instantiation: BigIntPrimitive.cpp:_ZN6hermes2vmL16makeTruncAdapterITnDaXadL_ZNS_6bigint6asIntNENS2_16MutableBigIntRefEmNS2_18ImmutableBigIntRefEEEEEDam
Unexecuted instantiation: BigIntPrimitive.cpp:_ZN6hermes2vmL16makeTruncAdapterITnDaXadL_ZNS_6bigint7asUintNENS2_16MutableBigIntRefEmNS2_18ImmutableBigIntRefEEEEEDam
92
93
CallResult<HermesValue> BigIntPrimitive::asIntN(
94
    Runtime &runtime,
95
    uint64_t n,
96
0
    Handle<BigIntPrimitive> src) {
97
0
  if (n == 0) {
98
0
    return BigIntPrimitive::fromSigned(runtime, 0);
99
0
  }
100
101
0
  const uint32_t numDigits =
102
0
      bigint::asIntNResultSize(n, src->getImmutableRef(runtime));
103
0
  return unaryOp(runtime, makeTruncAdapter<&bigint::asIntN>(n), src, numDigits);
104
0
}
105
106
CallResult<HermesValue> BigIntPrimitive::asUintN(
107
    Runtime &runtime,
108
    uint64_t n,
109
0
    Handle<BigIntPrimitive> src) {
110
0
  if (n == 0) {
111
0
    return BigIntPrimitive::fromSigned(runtime, 0);
112
0
  }
113
114
0
  uint32_t numDigits;
115
0
  bigint::OperationStatus status =
116
0
      bigint::asUintNResultSize(n, src->getImmutableRef(runtime), numDigits);
117
0
  if (LLVM_UNLIKELY(status != bigint::OperationStatus::RETURNED)) {
118
0
    return raiseOnError(runtime, status);
119
0
  }
120
0
  return unaryOp(
121
0
      runtime, makeTruncAdapter<&bigint::asUintN>(n), src, numDigits);
122
0
}
123
124
template <typename UnaryOpT>
125
CallResult<HermesValue> BigIntPrimitive::unaryOp(
126
    Runtime &runtime,
127
    UnaryOpT op,
128
    Handle<BigIntPrimitive> src,
129
1
    size_t numDigits) {
130
1
  auto u =
131
1
      BigIntPrimitive::createUninitializedWithNumDigits(runtime, numDigits);
132
133
1
  if (LLVM_UNLIKELY(u == ExecutionStatus::EXCEPTION)) {
134
0
    return ExecutionStatus::EXCEPTION;
135
0
  }
136
137
1
  auto res = (op)(u->getMutableRef(runtime), src->getImmutableRef(runtime));
138
1
  if (LLVM_UNLIKELY(res != bigint::OperationStatus::RETURNED)) {
139
0
    return raiseOnError(runtime, res);
140
0
  }
141
142
1
  return HermesValue::encodeBigIntValue(u->getBigIntPrimitive());
143
1
}
Unexecuted instantiation: BigIntPrimitive.cpp:_ZN6hermes2vm15BigIntPrimitive7unaryOpIZNS0_L16makeTruncAdapterITnDaXadL_ZNS_6bigint6asIntNENS4_16MutableBigIntRefEmNS4_18ImmutableBigIntRefEEEEEDamEUlS5_S6_E_EENS0_10CallResultINS0_11HermesValueELNS0_6detail20CallResultSpecializeE2EEERNS0_7RuntimeET_NS0_6HandleIS1_EEm
Unexecuted instantiation: BigIntPrimitive.cpp:_ZN6hermes2vm15BigIntPrimitive7unaryOpIZNS0_L16makeTruncAdapterITnDaXadL_ZNS_6bigint7asUintNENS4_16MutableBigIntRefEmNS4_18ImmutableBigIntRefEEEEEDamEUlS5_S6_E_EENS0_10CallResultINS0_11HermesValueELNS0_6detail20CallResultSpecializeE2EEERNS0_7RuntimeET_NS0_6HandleIS1_EEm
hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::BigIntPrimitive::unaryOp<hermes::bigint::OperationStatus (*)(hermes::bigint::MutableBigIntRef, hermes::bigint::ImmutableBigIntRef)>(hermes::vm::Runtime&, hermes::bigint::OperationStatus (*)(hermes::bigint::MutableBigIntRef, hermes::bigint::ImmutableBigIntRef), hermes::vm::Handle<hermes::vm::BigIntPrimitive>, unsigned long)
Line
Count
Source
129
1
    size_t numDigits) {
130
1
  auto u =
131
1
      BigIntPrimitive::createUninitializedWithNumDigits(runtime, numDigits);
132
133
1
  if (LLVM_UNLIKELY(u == ExecutionStatus::EXCEPTION)) {
134
0
    return ExecutionStatus::EXCEPTION;
135
0
  }
136
137
1
  auto res = (op)(u->getMutableRef(runtime), src->getImmutableRef(runtime));
138
1
  if (LLVM_UNLIKELY(res != bigint::OperationStatus::RETURNED)) {
139
0
    return raiseOnError(runtime, res);
140
0
  }
141
142
1
  return HermesValue::encodeBigIntValue(u->getBigIntPrimitive());
143
1
}
Unexecuted instantiation: BigIntPrimitive.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::BigIntPrimitive::unaryOp<hermes::vm::BigIntPrimitive::inc(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>)::$_0>(hermes::vm::Runtime&, hermes::vm::BigIntPrimitive::inc(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>)::$_0, hermes::vm::Handle<hermes::vm::BigIntPrimitive>, unsigned long)
Unexecuted instantiation: BigIntPrimitive.cpp:hermes::vm::CallResult<hermes::vm::HermesValue, (hermes::vm::detail::CallResultSpecialize)2> hermes::vm::BigIntPrimitive::unaryOp<hermes::vm::BigIntPrimitive::dec(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>)::$_0>(hermes::vm::Runtime&, hermes::vm::BigIntPrimitive::dec(hermes::vm::Runtime&, hermes::vm::Handle<hermes::vm::BigIntPrimitive>)::$_0, hermes::vm::Handle<hermes::vm::BigIntPrimitive>, unsigned long)
144
145
CallResult<HermesValue> BigIntPrimitive::unaryMinus(
146
    Runtime &runtime,
147
1
    Handle<BigIntPrimitive> src) {
148
1
  if (src->compare(0) == 0) {
149
0
    return HermesValue::encodeBigIntValue(*src);
150
0
  }
151
152
1
  const uint32_t numDigits =
153
1
      bigint::unaryMinusResultSize(src->getImmutableRef(runtime));
154
1
  return unaryOp(runtime, &bigint::unaryMinus, src, numDigits);
155
1
}
156
157
CallResult<HermesValue> BigIntPrimitive::unaryNOT(
158
    Runtime &runtime,
159
0
    Handle<BigIntPrimitive> src) {
160
0
  const uint32_t numDigits =
161
0
      bigint::unaryNotResultSize(src->getImmutableRef(runtime));
162
0
  return unaryOp(runtime, &bigint::unaryNot, src, numDigits);
163
0
}
164
165
CallResult<HermesValue> BigIntPrimitive::binaryOp(
166
    Runtime &runtime,
167
    BinaryOp op,
168
    Handle<BigIntPrimitive> lhs,
169
    Handle<BigIntPrimitive> rhs,
170
0
    uint32_t numDigitsResult) {
171
0
  auto u = BigIntPrimitive::createUninitializedWithNumDigits(
172
0
      runtime, numDigitsResult);
173
174
0
  if (LLVM_UNLIKELY(u == ExecutionStatus::EXCEPTION)) {
175
0
    return ExecutionStatus::EXCEPTION;
176
0
  }
177
178
0
  auto res = (*op)(
179
0
      u->getMutableRef(runtime),
180
0
      lhs->getImmutableRef(runtime),
181
0
      rhs->getImmutableRef(runtime));
182
0
  if (LLVM_UNLIKELY(res != bigint::OperationStatus::RETURNED)) {
183
0
    return raiseOnError(runtime, res);
184
0
  }
185
186
0
  return HermesValue::encodeBigIntValue(u->getBigIntPrimitive());
187
0
}
188
189
CallResult<HermesValue> BigIntPrimitive::add(
190
    Runtime &runtime,
191
    Handle<BigIntPrimitive> lhs,
192
0
    Handle<BigIntPrimitive> rhs) {
193
0
  const size_t numDigits = bigint::addResultSize(
194
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
195
0
  return binaryOp(runtime, &bigint::add, lhs, rhs, numDigits);
196
0
}
197
198
CallResult<HermesValue> BigIntPrimitive::subtract(
199
    Runtime &runtime,
200
    Handle<BigIntPrimitive> lhs,
201
0
    Handle<BigIntPrimitive> rhs) {
202
0
  const size_t numDigits = bigint::subtractResultSize(
203
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
204
0
  return binaryOp(runtime, &bigint::subtract, lhs, rhs, numDigits);
205
0
}
206
207
CallResult<HermesValue> BigIntPrimitive::multiply(
208
    Runtime &runtime,
209
    Handle<BigIntPrimitive> lhs,
210
0
    Handle<BigIntPrimitive> rhs) {
211
0
  const uint32_t numDigits = bigint::multiplyResultSize(
212
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
213
0
  return binaryOp(runtime, &bigint::multiply, lhs, rhs, numDigits);
214
0
}
215
216
CallResult<HermesValue> BigIntPrimitive::divide(
217
    Runtime &runtime,
218
    Handle<BigIntPrimitive> lhs,
219
0
    Handle<BigIntPrimitive> rhs) {
220
0
  const uint32_t numDigits = bigint::divideResultSize(
221
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
222
0
  return binaryOp(runtime, &bigint::divide, lhs, rhs, numDigits);
223
0
}
224
225
CallResult<HermesValue> BigIntPrimitive::remainder(
226
    Runtime &runtime,
227
    Handle<BigIntPrimitive> lhs,
228
0
    Handle<BigIntPrimitive> rhs) {
229
0
  const uint32_t numDigits = bigint::remainderResultSize(
230
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
231
0
  return binaryOp(runtime, &bigint::remainder, lhs, rhs, numDigits);
232
0
}
233
234
CallResult<HermesValue> BigIntPrimitive::exponentiate(
235
    Runtime &runtime,
236
    Handle<BigIntPrimitive> lhs,
237
0
    Handle<BigIntPrimitive> rhs) {
238
0
  uint32_t tmpDstSize = bigint::BigIntMaxSizeInDigits;
239
0
  bigint::TmpStorage tmpDst(tmpDstSize);
240
0
  bigint::MutableBigIntRef dst{tmpDst.requestNumDigits(tmpDstSize), tmpDstSize};
241
0
  auto res = bigint::exponentiate(
242
0
      dst, lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
243
0
  if (LLVM_UNLIKELY(res != bigint::OperationStatus::RETURNED)) {
244
0
    return raiseOnError(runtime, res);
245
0
  }
246
247
0
  auto ptr = reinterpret_cast<const uint8_t *>(dst.digits);
248
0
  uint32_t size = dst.numDigits * DigitSizeInBytes;
249
0
  return BigIntPrimitive::fromBytes(runtime, llvh::makeArrayRef(ptr, size));
250
0
}
251
252
CallResult<HermesValue> BigIntPrimitive::bitwiseAND(
253
    Runtime &runtime,
254
    Handle<BigIntPrimitive> lhs,
255
0
    Handle<BigIntPrimitive> rhs) {
256
0
  const uint32_t numDigits = bigint::bitwiseANDResultSize(
257
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
258
0
  return binaryOp(runtime, &bigint::bitwiseAND, lhs, rhs, numDigits);
259
0
}
260
261
CallResult<HermesValue> BigIntPrimitive::bitwiseOR(
262
    Runtime &runtime,
263
    Handle<BigIntPrimitive> lhs,
264
0
    Handle<BigIntPrimitive> rhs) {
265
0
  const uint32_t numDigits = bigint::bitwiseORResultSize(
266
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
267
0
  return binaryOp(runtime, &bigint::bitwiseOR, lhs, rhs, numDigits);
268
0
}
269
270
CallResult<HermesValue> BigIntPrimitive::bitwiseXOR(
271
    Runtime &runtime,
272
    Handle<BigIntPrimitive> lhs,
273
0
    Handle<BigIntPrimitive> rhs) {
274
0
  const uint32_t numDigits = bigint::bitwiseXORResultSize(
275
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
276
0
  return binaryOp(runtime, &bigint::bitwiseXOR, lhs, rhs, numDigits);
277
0
}
278
279
CallResult<HermesValue> BigIntPrimitive::leftShift(
280
    Runtime &runtime,
281
    Handle<BigIntPrimitive> lhs,
282
0
    Handle<BigIntPrimitive> rhs) {
283
0
  const uint32_t numDigits = bigint::leftShiftResultSize(
284
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
285
0
  return binaryOp(runtime, &bigint::leftShift, lhs, rhs, numDigits);
286
0
}
287
288
CallResult<HermesValue> BigIntPrimitive::signedRightShift(
289
    Runtime &runtime,
290
    Handle<BigIntPrimitive> lhs,
291
0
    Handle<BigIntPrimitive> rhs) {
292
0
  const uint32_t numDigits = bigint::signedRightShiftResultSize(
293
0
      lhs->getImmutableRef(runtime), rhs->getImmutableRef(runtime));
294
0
  return binaryOp(runtime, &bigint::signedRightShift, lhs, rhs, numDigits);
295
0
}
296
297
CallResult<HermesValue> BigIntPrimitive::unsignedRightShift(
298
    Runtime &runtime,
299
    Handle<BigIntPrimitive> lhs,
300
0
    Handle<BigIntPrimitive> rhs) {
301
0
  return runtime.raiseTypeError("BigInts have no unsigned shift");
302
0
}
303
304
CallResult<HermesValue> BigIntPrimitive::inc(
305
    Runtime &runtime,
306
0
    Handle<BigIntPrimitive> src) {
307
0
  auto incAdapter = [](bigint::MutableBigIntRef dst,
308
0
                       bigint::ImmutableBigIntRef lhs) {
309
0
    constexpr bigint::SignedBigIntDigitType one = 1ll;
310
0
    return bigint::addSigned(dst, lhs, one);
311
0
  };
312
313
0
  const size_t numDigits =
314
0
      bigint::addSignedResultSize(src->getImmutableRef(runtime), 1);
315
0
  return unaryOp(runtime, incAdapter, src, numDigits);
316
0
}
317
318
CallResult<HermesValue> BigIntPrimitive::dec(
319
    Runtime &runtime,
320
0
    Handle<BigIntPrimitive> src) {
321
0
  auto decAdapter = [](bigint::MutableBigIntRef dst,
322
0
                       bigint::ImmutableBigIntRef lhs) {
323
0
    constexpr bigint::SignedBigIntDigitType one = 1ll;
324
0
    return bigint::subtractSigned(dst, lhs, one);
325
0
  };
326
327
0
  const size_t numDigits =
328
0
      bigint::subtractSignedResultSize(src->getImmutableRef(runtime), 1);
329
0
  return unaryOp(runtime, decAdapter, src, numDigits);
330
0
}
331
332
0
CallResult<double> BigIntPrimitive::toDouble(Runtime &runtime) const {
333
0
  double val;
334
0
  ExecutionStatus res = raiseOnError(
335
0
      runtime, bigint::toDouble(val, this->getImmutableRefUnsafe()));
336
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
337
0
    return ExecutionStatus::EXCEPTION;
338
0
  }
339
340
0
  return val;
341
0
}
342
343
} // namespace vm
344
} // namespace hermes