/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 |