1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/base/number_helper.h"
17
18 #include <cfenv>
19 #include <sys/time.h>
20 #include <unistd.h>
21
22 #include "common_components/objects/string_table/integer_cache.h"
23 #include "ecmascript/base/dtoa_helper.h"
24 #include "ecmascript/base/string_helper.h"
25 #include "ecmascript/builtins/builtins_number.h"
26 #include "ecmascript/ecma_string-inl.h"
27 #include "ecmascript/ecma_string_table.h"
28 #include "ecmascript/global_env.h"
29 #include "ecmascript/js_tagged_value-inl.h"
30
31 namespace panda::ecmascript::base {
32 using NumberToStringResultCache = builtins::NumberToStringResultCache;
33
34 enum class Sign { NONE, NEG, POS };
35 thread_local uint64_t RandomGenerator::randomState_ {0};
36 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
37 #define RETURN_IF_CONVERSION_END(p, end, result) \
38 if ((p) == (end)) { \
39 return (result); \
40 }
41
42 constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz"; // NOLINT (modernize-avoid-c-arrays)
43 constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U;
44
45 static const double POWERS_OF_TEN[] = {
46 1.0, // 10^0
47 10.0,
48 100.0,
49 1000.0,
50 10000.0,
51 100000.0,
52 1000000.0,
53 10000000.0,
54 100000000.0,
55 1000000000.0,
56 10000000000.0, // 10^10
57 100000000000.0,
58 1000000000000.0,
59 10000000000000.0,
60 100000000000000.0,
61 1000000000000000.0,
62 10000000000000000.0,
63 100000000000000000.0,
64 1000000000000000000.0,
65 10000000000000000000.0,
66 100000000000000000000.0, // 10^20
67 1000000000000000000000.0,
68 10000000000000000000000.0 // 10^22
69 };
70 static const int POWERS_OF_TEN_SIZE = 23;
71 const CString NumberHelper::NAN_STR = "NaN";
72 const CString NumberHelper::ZERO_STR = "0";
73 const CString NumberHelper::MINUS_INFINITY_STR = "-Infinity";
74 const CString NumberHelper::INFINITY_STR = "Infinity";
75
ToDigit(uint8_t c)76 static inline uint8_t ToDigit(uint8_t c)
77 {
78 if (c >= '0' && c <= '9') {
79 return c - '0';
80 }
81 if (c >= 'A' && c <= 'Z') {
82 return c - 'A' + DECIMAL;
83 }
84 if (c >= 'a' && c <= 'z') {
85 return c - 'a' + DECIMAL;
86 }
87 return '$';
88 }
89
GotoNonspace(uint8_t ** ptr,const uint8_t * end)90 bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end)
91 {
92 while (*ptr < end) {
93 uint16_t c = **ptr;
94 size_t size = 1;
95 if (c > INT8_MAX) {
96 size = 0;
97 uint16_t utf8Bit = INT8_MAX + 1; // equal 0b1000'0000
98 while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) {
99 ++size;
100 utf8Bit >>= 1UL;
101 }
102 if (common::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, end - *ptr, 1) <= 0) {
103 return true;
104 }
105 }
106 if (!StringHelper::IsNonspace(c)) {
107 return true;
108 }
109 *ptr += size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
110 }
111 return false;
112 }
113
SignedZero(Sign sign)114 static inline double SignedZero(Sign sign)
115 {
116 return sign == Sign::NEG ? -0.0 : 0.0;
117 }
118
IsEmptyString(const uint8_t * start,const uint8_t * end)119 bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
120 {
121 auto p = const_cast<uint8_t *>(start);
122 return !NumberHelper::GotoNonspace(&p, end);
123 }
124
125 /*
126 * This Function Translate from number 0-9 to number '0'-'9'
127 * number 10-35 to number 'a'-'z'
128 */
ToCharCode(uint32_t number)129 uint32_t NumberHelper::ToCharCode(uint32_t number)
130 {
131 ASSERT(number < 36); // 36 == total number of '0'-'9' + 'a' -'z'
132 return number < 10 ? (number + 48): // 48 == '0'; 10: '0' - '9';
133 (number - 10 + 97); // 97 == 'a'; 'a' - 'z'
134 }
135
Int32ToString(JSThread * thread,int32_t number,uint32_t radix)136 JSTaggedValue NumberHelper::Int32ToString(JSThread *thread, int32_t number, uint32_t radix)
137 {
138 bool isNegative = number < 0;
139 uint32_t n = 0;
140 if (!isNegative) {
141 n = static_cast<uint32_t>(number);
142 if (n < radix) {
143 if (n == 0) {
144 return thread->GlobalConstants()->GetHandledZeroString().GetTaggedValue();
145 }
146 JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
147 return singleCharTable->GetStringFromSingleCharTable(thread, ToCharCode(n));
148 }
149 } else {
150 n = static_cast<uint32_t>(-number);
151 }
152 uint32_t temp = n;
153 uint32_t length = isNegative ? 1 : 0;
154 // calculate length
155 while (temp > 0) {
156 temp = temp / radix;
157 length = length + 1;
158 }
159 std::string buf;
160 buf.resize(length);
161 ASSERT(length > 0);
162 uint32_t index = length - 1;
163 uint32_t digit = 0;
164 while (n > 0) {
165 digit = n % radix;
166 n /= radix;
167 buf[index] = ToCharCode(digit) + 0X00;
168 index--;
169 }
170 if (isNegative) {
171 ASSERT(index == 0);
172 buf[index] = '-';
173 }
174 return thread->GetEcmaVM()->GetFactory()->NewFromUtf8(buf).GetTaggedValue();
175 }
176
DoubleToString(JSThread * thread,double number,int radix)177 JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
178 {
179 static constexpr int BUFFER_SIZE = 2240; // 2240: The size of the character array buffer
180 static constexpr int HALF_BUFFER_SIZE = BUFFER_SIZE >> 1;
181 char buffer[BUFFER_SIZE];
182 size_t integerCursor = HALF_BUFFER_SIZE;
183 size_t fractionCursor = integerCursor;
184
185 bool negative = number < 0.0;
186 if (negative) {
187 number = -number;
188 }
189
190 double integer = std::floor(number);
191 double fraction = number - integer;
192
193 auto value = bit_cast<uint64_t>(number);
194 value += 1;
195 double delta = HALF * (bit_cast<double>(value) - number);
196 delta = std::max(delta, bit_cast<double>(static_cast<uint64_t>(1))); // 1 : The binary of the smallest double is 1
197 if (fraction != 0 && fraction >= delta) {
198 buffer[fractionCursor++] = '.';
199 while (fraction >= delta) {
200 fraction *= radix;
201 delta *= radix;
202 int64_t digit = std::floor(fraction);
203 fraction -= digit;
204 buffer[fractionCursor++] = CHARS[digit];
205 bool needCarry = (fraction > HALF) && (fraction + delta > 1);
206 if (needCarry) {
207 size_t fractionEnd = fractionCursor - 1;
208 buffer[fractionEnd] = Carry(buffer[fractionEnd], radix);
209 for (; fractionEnd > HALF_BUFFER_SIZE; fractionEnd--) {
210 if (buffer[fractionEnd] == '0') {
211 buffer[fractionEnd - 1] = Carry(buffer[fractionEnd - 1], radix);
212 } else {
213 break;
214 }
215 }
216 if (fractionEnd == HALF_BUFFER_SIZE) {
217 ++integer;
218 }
219 break;
220 }
221 }
222 // delete 0 in the end
223 size_t fractionEnd = fractionCursor - 1;
224 while (buffer[fractionEnd] == '0') {
225 --fractionEnd;
226 }
227 fractionCursor = fractionEnd + 1;
228 }
229
230 ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
231 while (Exponent(integer / radix) > 0) {
232 integer /= radix;
233 buffer[--integerCursor] = '0';
234 }
235 do {
236 double remainder = std::fmod(integer, radix);
237 buffer[--integerCursor] = CHARS[static_cast<int>(remainder)];
238 integer = (integer - remainder) / radix;
239 } while (integer > 0);
240
241 if (negative) {
242 buffer[--integerCursor] = '-';
243 }
244 buffer[fractionCursor++] = '\0';
245
246 size_t size = fractionCursor - integerCursor;
247 std::unique_ptr<char[]> result = std::make_unique<char[]>(size);
248 if (memcpy_s(result.get(), size, buffer + integerCursor, size) != EOK) {
249 LOG_FULL(FATAL) << "memcpy_s failed";
250 UNREACHABLE();
251 }
252 return BuiltinsBase::GetTaggedString(thread, result.get());
253 }
254
DoubleToFixedString(JSThread * thread,double valueNumber,int digitNumber)255 JSTaggedValue NumberHelper::DoubleToFixedString(JSThread *thread, double valueNumber, int digitNumber)
256 {
257 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
258 bool negative = false;
259 double absValue = valueNumber;
260 std::string result;
261
262 if (valueNumber < 0) {
263 result += "-";
264 absValue = -valueNumber;
265 negative = true;
266 }
267 int decimalPoint;
268 const int decimalRepCapacity = MAX_DIGITS + MAX_FRACTION + 1; // Add space for the '\0' byte.
269 char decimalRep[decimalRepCapacity];
270 int length;
271 bool isFast = DtoaHelper::FixedDtoa(absValue, digitNumber,
272 BufferVector<char>(decimalRep, decimalRepCapacity), &length, &decimalPoint);
273 if (!isFast) {
274 return DoubleToASCII(thread, valueNumber, digitNumber, base::FRAC_FORMAT); // slow
275 }
276 int zeroPrefixLen = 0;
277 int zeroPostfixLen = 0;
278 if (decimalPoint <= 0) {
279 zeroPrefixLen = -decimalPoint + 1;
280 decimalPoint = 1;
281 }
282 if (zeroPrefixLen + length < decimalPoint + digitNumber) {
283 zeroPostfixLen = decimalPoint + digitNumber - length - zeroPrefixLen;
284 }
285 result += std::string(zeroPrefixLen, '0');
286 result += decimalRep;
287 result += std::string(zeroPostfixLen, '0');
288 if (digitNumber > 0) {
289 if (negative) {
290 result.insert(decimalPoint + 1, 1, '.');
291 } else {
292 result.insert(decimalPoint, 1, '.');
293 }
294 }
295 return factory->NewFromASCII(result.c_str()).GetTaggedValue();
296 }
297
DoubleToASCII(JSThread * thread,double valueNumber,int digitNumber,int flags)298 JSTaggedValue NumberHelper::DoubleToASCII(JSThread *thread, double valueNumber, int digitNumber, int flags)
299 {
300 std::string buffer(JS_DTOA_BUF_SIZE, '\0');
301 DoubleToASCIIWithFlag(buffer, valueNumber, digitNumber, flags);
302 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
303 return factory->NewFromASCII(buffer.c_str()).GetTaggedValue();
304 }
305
GetBaseForRoundingMode(double valueNumber,int digitNumber,int * decimalPoint,std::string & buf,std::string & buf1,int buf1Size,int roundingMode,int * sign)306 void NumberHelper::GetBaseForRoundingMode(double valueNumber, int digitNumber, int *decimalPoint, std::string& buf,
307 std::string& buf1, int buf1Size, int roundingMode, int *sign)
308 {
309 if (roundingMode != FE_TONEAREST) {
310 fesetround(roundingMode);
311 }
312 int result = snprintf_s(&buf1[0], buf1Size, buf1Size - 1, "%+.*e", digitNumber - 1, valueNumber);
313 if (result == -1) {
314 LOG_FULL(FATAL) << "snprintf_s failed";
315 UNREACHABLE();
316 }
317 if (roundingMode != FE_TONEAREST) {
318 fesetround(FE_TONEAREST);
319 }
320 *sign = (buf1[0] == '-');
321 buf[0] = buf1[1];
322 if (digitNumber > 1) {
323 if (memcpy_s(&buf[1], digitNumber - 1, &buf1[POINT_INDEX], digitNumber - 1) != EOK) {
324 LOG_FULL(FATAL) << "memcpy_s failed";
325 UNREACHABLE();
326 }
327 }
328 buf[digitNumber] = '\0';
329 *decimalPoint = std::atoi(&buf1[digitNumber + DECIMAL_INDEX + (digitNumber > 1)]) + 1;
330 }
331
CustomEcvtIsFixed(double & valueNumber,int & digits,int * decimalPoint,std::string & buf,int * sign)332 void NumberHelper::CustomEcvtIsFixed(double &valueNumber, int &digits, int *decimalPoint, std::string& buf, int *sign)
333 {
334 std::string buffer(JS_DTOA_BUF_SIZE, '\0');
335 unsigned int digitsMin = 1;
336 unsigned int digitsMax = DOUBLE_MAX_PRECISION;
337 while (digitsMin < digitsMax) {
338 digits = (digitsMin + digitsMax) / MIN_RADIX;
339 ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
340 GetBaseForRoundingMode(valueNumber, digits, decimalPoint, buf, buffer, JS_DTOA_BUF_SIZE, FE_TONEAREST, sign);
341 if (std::strtod(buffer.c_str(), NULL) == valueNumber) {
342 while (digits >= MIN_RADIX && buf[digits - 1] == '0') {
343 digits--;
344 }
345 digitsMax = static_cast<unsigned int>(digits);
346 } else {
347 digitsMin = static_cast<unsigned int>(digits) + 1;
348 }
349 }
350 digits = static_cast<int>(digitsMax);
351 }
352
CustomEcvt(double valueNumber,int digits,int * decimalPoint,std::string & buf,bool isFixed,int * sign)353 int NumberHelper::CustomEcvt(double valueNumber, int digits, int *decimalPoint,
354 std::string& buf, bool isFixed, int *sign)
355 {
356 std::string buffer(JS_DTOA_BUF_SIZE, '\0');
357 int roundingMode = FE_TONEAREST;
358 if (!isFixed) {
359 CustomEcvtIsFixed(valueNumber, digits, decimalPoint, buf, sign);
360 } else {
361 std::string buf1(JS_DTOA_BUF_SIZE, '\0');
362 std::string buf2(JS_DTOA_BUF_SIZE, '\0');
363 int decpt1 = 0;
364 int decpt2 = 0;
365 int sign1 = 0;
366 int sign2 = 0;
367 ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
368 GetBaseForRoundingMode(valueNumber, digits + 1, &decpt1, buf1, buffer, JS_DTOA_BUF_SIZE, roundingMode, &sign1);
369 if (buf1[digits] == HALFCHAR) {
370 ASSERT(buf1.size() <= JS_DTOA_BUF_SIZE);
371 GetBaseForRoundingMode(valueNumber, digits + 1, &decpt1, buf1, buffer, JS_DTOA_BUF_SIZE,
372 FE_DOWNWARD, &sign1);
373 ASSERT(buf2.size() <= JS_DTOA_BUF_SIZE);
374 GetBaseForRoundingMode(valueNumber, digits + 1, &decpt2, buf2, buffer, JS_DTOA_BUF_SIZE,
375 FE_UPWARD, &sign2);
376 if (memcmp(buf1.c_str(), buf2.c_str(), digits + 1) == 0 && decpt1 == decpt2) {
377 roundingMode = sign1 ? FE_DOWNWARD : FE_UPWARD;
378 }
379 }
380 }
381 ASSERT(buffer.size() <= JS_DTOA_BUF_SIZE);
382 GetBaseForRoundingMode(valueNumber, digits, decimalPoint, buf, buffer, JS_DTOA_BUF_SIZE, roundingMode, sign);
383 return digits;
384 }
385
CustomFcvtHelper(std::string & buf,int bufSize,double valueNumber,int digits,int roundingMode)386 int NumberHelper::CustomFcvtHelper(std::string& buf, int bufSize, double valueNumber, int digits, int roundingMode)
387 {
388 if (roundingMode != FE_TONEAREST) {
389 std::fesetround(roundingMode);
390 }
391 int result = snprintf_s(&buf[0], bufSize, bufSize, "%.*f", digits, valueNumber);
392 if (result == -1) {
393 LOG_FULL(FATAL) << "snprintf_s failed";
394 UNREACHABLE();
395 }
396 if (roundingMode != FE_TONEAREST) {
397 std::fesetround(FE_TONEAREST);
398 }
399 ASSERT(result < bufSize);
400 return result;
401 }
402
CustomFcvt(std::string & buf,int bufSize,double valueNumber,int digits)403 void NumberHelper::CustomFcvt(std::string& buf, int bufSize, double valueNumber, int digits)
404 {
405 int number = 0;
406 int tmpNumber = 0;
407 std::string tmpbuf1(JS_DTOA_BUF_SIZE, '\0');
408 std::string tmpbuf2(JS_DTOA_BUF_SIZE, '\0');
409 int roundingMode = FE_TONEAREST;
410 number = CustomFcvtHelper(tmpbuf1, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, roundingMode);
411 if (tmpbuf1[number - 1] == HALFCHAR) {
412 number = CustomFcvtHelper(tmpbuf1, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, FE_DOWNWARD);
413 tmpNumber = CustomFcvtHelper(tmpbuf2, JS_DTOA_BUF_SIZE, valueNumber, digits + 1, FE_UPWARD);
414 if (tmpbuf1 == tmpbuf2) {
415 if (tmpbuf1[0] == '-') {
416 roundingMode = FE_DOWNWARD;
417 } else {
418 roundingMode = FE_UPWARD;
419 }
420 }
421 }
422 CustomFcvtHelper(buf, bufSize, valueNumber, digits, roundingMode);
423 }
424
DoubleToPrecisionString(JSThread * thread,double number,int digit)425 JSTaggedValue NumberHelper::DoubleToPrecisionString(JSThread *thread, double number, int digit)
426 {
427 if (number == 0.0) {
428 return DoubleToFixedString(thread, number, digit - 1);
429 }
430 double positiveNumber = number > 0 ? number : -number;
431 int logDigit = std::floor(log10(positiveNumber));
432 int radixDigit = digit - logDigit - 1;
433 const int MIN_EXPONENT_DIGIT = -6;
434 if ((logDigit >= MIN_EXPONENT_DIGIT && logDigit < digit)) {
435 return DoubleToFixedString(thread, number, std::abs(radixDigit));
436 }
437 return DoubleToExponential(thread, number, digit);
438 }
439
DoubleToExponential(JSThread * thread,double number,int digit)440 JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit)
441 {
442 char tmpbuf[JS_DTOA_BUF_SIZE] = {0};
443 // Can use std::to_chars for performance.
444 if (digit == 0) {
445 if (number == 0.0) {
446 return BuiltinsBase::GetTaggedString(thread, "0e+0");
447 }
448 std::string res;
449 if (number < 0) {
450 res += "-";
451 number = -number;
452 }
453 int n;
454 int k;
455 DtoaHelper::Dtoa(number, tmpbuf, &n, &k);
456 std::string base = tmpbuf;
457 base.erase(1, k - 1);
458 if (k != 1) {
459 base += std::string(".") + std::string(tmpbuf + 1);
460 }
461 base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
462 res += base;
463 return BuiltinsBase::GetTaggedString(thread, res.c_str());
464 } else {
465 int result = snprintf_s(tmpbuf, sizeof(tmpbuf), sizeof(tmpbuf) - 1, "%.*e", digit - 1, number);
466 if (result == -1) {
467 LOG_FULL(FATAL) << "snprintf_s failed";
468 UNREACHABLE();
469 }
470 }
471 std::string result = tmpbuf;
472 size_t found = result.find_last_of('e');
473 if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') { // 2:offset of e
474 result.erase(found + 2, 1); // 2:offset of e
475 }
476 if (digit < 0) {
477 size_t end = found;
478 while (--found > 0) {
479 if (result[found] != '0') {
480 break;
481 }
482 }
483 if (result[found] == '.') {
484 found--;
485 }
486 if (found < end - 1) {
487 result.erase(found + 1, end - found - 1);
488 }
489 }
490 return BuiltinsBase::GetTaggedString(thread, result.c_str());
491 }
492
DoubleToASCIIWithFlag(std::string & buf,double valueNumber,int digits,int flags)493 void NumberHelper::DoubleToASCIIWithFlag(std::string& buf, double valueNumber, int digits, int flags)
494 {
495 if (valueNumber == 0.0) {
496 valueNumber = 0.0;
497 }
498 if (flags == FRAC_FORMAT) {
499 CustomFcvt(buf, JS_DTOA_BUF_SIZE, valueNumber, digits);
500 } else {
501 std::string buf1(JS_DTOA_BUF_SIZE, '\0');
502 int decimalPoint = 0;
503 int sign = 0;
504 bool fixed = ((static_cast<unsigned int>(flags) & POINT_INDEX) ==
505 static_cast<unsigned int>(base::FIXED_FORMAT));
506 int numberMax = fixed ? digits : MAX_DIGITS;
507 int digitNumber = CustomEcvt(valueNumber, digits, &decimalPoint, buf1, fixed, &sign);
508 int number = decimalPoint;
509 std::string tmpbuf;
510 int i = 0;
511 if (sign) {
512 tmpbuf += '-';
513 }
514 if (number > 0 && number <= numberMax) {
515 ToASCIIWithGreatThanZero(tmpbuf, digitNumber, number, buf1);
516 } else if (MIN_DIGITS < number && number <= 0) {
517 tmpbuf += '0';
518 tmpbuf += '.';
519 for (i = 0; i < -number; i++) {
520 tmpbuf += '0';
521 }
522 tmpbuf += buf1.substr(0, digitNumber);
523 } else {
524 ToASCIIWithNegative(tmpbuf, digitNumber, number, buf1);
525 }
526 buf = tmpbuf;
527 }
528 }
529
ToASCIIWithGreatThanZero(std::string & tmpbuf,int digitNumber,int number,const std::string & buf)530 void NumberHelper::ToASCIIWithGreatThanZero(std::string& tmpbuf, int digitNumber, int number, const std::string& buf)
531 {
532 if (digitNumber <= number) {
533 tmpbuf += buf.substr(0, digitNumber);
534 tmpbuf += std::string(number - digitNumber, '0');
535 tmpbuf += '\0';
536 } else {
537 tmpbuf += buf.substr(0, number);
538 tmpbuf += '.';
539 tmpbuf += buf.substr(number, digitNumber - number);
540 tmpbuf += '\0';
541 }
542 }
543
ToASCIIWithNegative(std::string & tmpbuf,int digitNumber,int n,const std::string & buf)544 void NumberHelper::ToASCIIWithNegative(std::string& tmpbuf, int digitNumber, int n, const std::string& buf)
545 {
546 tmpbuf += buf[0];
547 if (digitNumber > 1) {
548 tmpbuf += '.';
549 for (int i = 1; i < digitNumber; i++) {
550 tmpbuf += buf[i];
551 }
552 }
553 tmpbuf += 'e';
554 int p = n - 1;
555 if (p >= 0) {
556 tmpbuf += '+';
557 }
558 tmpbuf += std::to_string(p);
559 }
560
StringToNumber(JSThread * thread,EcmaString * string,int32_t radix)561 JSTaggedValue NumberHelper::StringToNumber(JSThread *thread, EcmaString *string, int32_t radix)
562 {
563 bool negative = false;
564 if ((radix == base::DECIMAL || radix == 0)) {
565 int32_t elementIndex = 0;
566 if (EcmaStringAccessor(string).ToInt(thread, &elementIndex, &negative)) {
567 if (elementIndex == 0 && negative == true) {
568 return JSTaggedValue(-0.0);
569 }
570 return JSTaggedValue(elementIndex);
571 }
572 }
573 CVector<uint8_t> buf;
574 Span<const uint8_t> str = EcmaStringAccessor(string).ToUtf8Span(thread, buf);
575
576 JSTaggedValue result = NumberHelper::StringToDoubleWithRadix(str.begin(), str.end(), radix, &negative);
577 if (result.GetNumber() == 0 && negative == true) {
578 return JSTaggedValue(-0.0);
579 }
580 return JSTaggedValue::TryCastDoubleToInt32(result.GetNumber());
581 }
582
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix,bool * negative)583 JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix, bool *negative)
584 {
585 auto p = const_cast<uint8_t *>(start);
586 JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE);
587 // 1. skip space and line terminal
588 if (!NumberHelper::GotoNonspace(&p, end)) {
589 return nanResult;
590 }
591
592 // 2. sign bit
593 if (*p == '-') {
594 *negative = true;
595 RETURN_IF_CONVERSION_END(++p, end, nanResult);
596 } else if (*p == '+') {
597 RETURN_IF_CONVERSION_END(++p, end, nanResult);
598 }
599 // 3. 0x or 0X
600 bool stripPrefix = true;
601 // 4. If R 0, then
602 // a. If R < 2 or R > 36, return NaN.
603 // b. If R 16, let stripPrefix be false.
604 if (radix != 0) {
605 if (radix < MIN_RADIX || radix > MAX_RADIX) {
606 return nanResult;
607 }
608 if (radix != HEXADECIMAL) {
609 stripPrefix = false;
610 }
611 } else {
612 radix = DECIMAL;
613 }
614 int size = 0;
615 if (stripPrefix) {
616 if (*p == '0') {
617 if (++p != end && (*p == 'x' || *p == 'X')) {
618 RETURN_IF_CONVERSION_END(++p, end, nanResult);
619 radix = HEXADECIMAL;
620 } else {
621 size++;
622 }
623 }
624 }
625
626 double result = 0;
627 bool isDone = false;
628 do {
629 double part = 0;
630 uint32_t multiplier = 1;
631 for (; p != end; ++p) {
632 // The maximum value to ensure that uint32_t will not overflow
633 const uint32_t MAX_MULTIPER = 0xffffffffU / 36;
634 uint32_t m = multiplier * static_cast<uint32_t>(radix);
635 if (m > MAX_MULTIPER) {
636 break;
637 }
638
639 int currentBit = static_cast<int>(ToDigit(*p));
640 if (currentBit >= radix) {
641 isDone = true;
642 break;
643 }
644 size++;
645 part = part * radix + currentBit;
646 multiplier = m;
647 }
648 result = result * multiplier + part;
649 if (isDone) {
650 break;
651 }
652 } while (p != end);
653
654 if (size == 0) {
655 return nanResult;
656 }
657
658 if (*negative) {
659 result = -result;
660 }
661 return BuiltinsBase::GetTaggedDouble(result);
662 }
663
Carry(char current,int radix)664 char NumberHelper::Carry(char current, int radix)
665 {
666 int digit = static_cast<int>((current > '9') ? (current - 'a' + DECIMAL) : (current - '0'));
667 digit = (digit == (radix - 1)) ? 0 : digit + 1;
668 return CHARS[digit];
669 }
670
IntegerToString(double number,int radix)671 CString NumberHelper::IntegerToString(double number, int radix)
672 {
673 ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
674 CString result;
675 while (number / radix > MAX_MANTISSA) {
676 number /= radix;
677 result = CString("0").append(result);
678 }
679 do {
680 double remainder = std::fmod(number, radix);
681 result = CHARS[static_cast<int>(remainder)] + result;
682 number = (number - remainder) / radix;
683 } while (number > 0);
684 return result;
685 }
686
IntToString(int number)687 CString NumberHelper::IntToString(int number)
688 {
689 return ToCString(number);
690 }
691
IntToEcmaString(const JSThread * thread,int number)692 JSHandle<EcmaString> NumberHelper::IntToEcmaString(const JSThread *thread, int number)
693 {
694 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
695 return factory->NewFromASCII(ToCString(number));
696 }
697
DoubleToCString(double d)698 CString NumberHelper::DoubleToCString(double d)
699 {
700 if (std::isnan(d)) {
701 return NumberHelper::NAN_STR;
702 }
703 if (d == 0.0) {
704 return NumberHelper::ZERO_STR;
705 }
706 if (std::isinf(d)) {
707 if (d < 0) {
708 return NumberHelper::MINUS_INFINITY_STR;
709 } else {
710 return NumberHelper::INFINITY_STR;
711 }
712 }
713 bool isNeg = false;
714 if (d < 0) {
715 isNeg = true;
716 d = -d;
717 }
718 ASSERT(d > 0);
719 constexpr int startIdx = 8;
720 char buff[JS_DTOA_BUF_SIZE] = {0};
721 char *result = buff + startIdx;
722 int n; // decimal point
723 int k; // length
724 DtoaHelper::Dtoa(d, result, &n, &k); //Fast Double To Ascii.
725 if (n > 0 && n <= MAX_DIGITS) {
726 if (k <= n) {
727 // 6. If k ≤ n ≤ 21
728 for (int i = k; i < n; ++i) {
729 result[i] = '0';
730 }
731 } else {
732 // 7. If 0 < n ≤ 21
733 --result;
734 for (int i = 0; i < n; ++i) {
735 result[i] = result[i + 1];
736 }
737 result[n] = '.';
738 }
739 } else if (MIN_DIGITS < n && n <= 0) {
740 // 8. If −6 < n ≤ 0
741 constexpr int prefixLen = 2;
742 result -= (-n + prefixLen);
743 result[0] = '0';
744 result[1] = '.';
745 for (int i = prefixLen; i < -n + prefixLen; ++i) {
746 result[i] = '0';
747 }
748 } else {
749 // 9. & 10. Otherwise
750 int pos = k;
751 if (k != 1) {
752 --result;
753 result[0] = result[1];
754 result[1] = '.';
755 ++pos;
756 }
757 result[pos++] = 'e';
758 if (n >= 1) {
759 result[pos++] = '+';
760 }
761 auto expo = std::to_string(n - 1);
762 auto p = expo.c_str();
763 for (size_t i = 0; i < expo.length(); ++i) {
764 result[pos++] = p[i];
765 }
766 }
767 if (isNeg) {
768 --result;
769 result[0] = '-';
770 }
771 return result;
772 }
773
774 // 7.1.12.1 ToString Applied to the Number Type
NumberToString(const JSThread * thread,JSTaggedValue number)775 JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
776 {
777 ASSERT(number.IsNumber());
778 JSHandle<NumberToStringResultCache> cacheTable(thread->GetGlobalEnv()->GetNumberToStringResultCache());
779 int entry = cacheTable->GetNumberHash(number);
780 JSTaggedValue cacheResult = cacheTable->FindCachedResult(thread, entry, number);
781 if (cacheResult != JSTaggedValue::Undefined()) {
782 return JSHandle<EcmaString>::Cast(JSHandle<JSTaggedValue>(thread, cacheResult));
783 }
784
785 JSHandle<EcmaString> resultJSHandle;
786 if (number.IsInt()) {
787 int intVal = number.GetInt();
788 if (intVal == 0) {
789 resultJSHandle = JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
790 } else {
791 resultJSHandle = IntToEcmaString(thread, intVal);
792 }
793 } else {
794 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
795 resultJSHandle = factory->NewFromASCIISkippingStringTable(DoubleToCString(number.GetDouble()));
796 }
797
798 cacheTable->SetCachedResult(thread, entry, number, resultJSHandle);
799 return resultJSHandle;
800 }
801
TruncateDouble(double d)802 double NumberHelper::TruncateDouble(double d)
803 {
804 if (std::isnan(d)) {
805 return 0;
806 }
807 if (!std::isfinite(d)) {
808 return d;
809 }
810 // -0 to +0
811 if (d == 0.0) {
812 return 0;
813 }
814 double ret = (d >= 0) ? std::floor(d) : std::ceil(d);
815 if (ret == 0.0) {
816 ret = 0;
817 }
818 return ret;
819 }
820
DoubleToInt64(double d)821 int64_t NumberHelper::DoubleToInt64(double d)
822 {
823 if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
824 return std::numeric_limits<int64_t>::max();
825 }
826 if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
827 return std::numeric_limits<int64_t>::min();
828 }
829 return static_cast<int64_t>(d);
830 }
831
DoubleToUInt64(double d)832 uint64_t NumberHelper::DoubleToUInt64(double d)
833 {
834 ASSERT(d <= static_cast<double>(std::numeric_limits<uint64_t>::max()) &&
835 d >= static_cast<double>(std::numeric_limits<uint64_t>::min()));
836 return static_cast<uint64_t>(d);
837 }
838
IsDigitalString(const uint8_t * start,const uint8_t * end)839 bool NumberHelper::IsDigitalString(const uint8_t *start, const uint8_t *end)
840 {
841 int len = end - start;
842 for (int i = 0; i < len; i++) {
843 if (*(start + i) < '0' || *(start + i) > '9') {
844 return false;
845 }
846 }
847 return true;
848 }
849
StringToInt(const uint8_t * start,const uint8_t * end)850 int NumberHelper::StringToInt(const uint8_t *start, const uint8_t *end)
851 {
852 int num = *start - '0';
853 for (int i = 1; i < (end - start); i++) {
854 num = 10 * num + (*(start + i) - '0'); // 10 : 10 represents the base of the decimal system
855 }
856 return num;
857 }
858
859 // only for string is ordinary string and using UTF8 encoding
860 // Fast path for short integer and some special value
FastStringToNumber(const uint8_t * start,const uint8_t * end,common::IntegerCache * cache)861 std::pair<bool, JSTaggedNumber> NumberHelper::FastStringToNumber(const uint8_t *start,
862 const uint8_t *end, common::IntegerCache *cache)
863 {
864 ASSERT(start < end);
865 bool minus = (start[0] == '-');
866 int pos = (minus ? 1 : 0);
867
868 if (pos == (end - start)) {
869 return {true, JSTaggedNumber(NAN_VALUE)};
870 }
871 if (*(start + pos) > '9') {
872 // valid number's codes not longer than '9', except 'I' and non-breaking space.
873 if (*(start + pos) != 'I' && *(start + pos) != 0xA0) {
874 return {true, JSTaggedNumber(NAN_VALUE)};
875 }
876 } else if ((end - (start + pos)) <= MAX_ELEMENT_INDEX_LEN && IsDigitalString((start + pos), end)) {
877 int num = StringToInt((start + pos), end);
878 if LIKELY(!minus) {
879 if (cache != nullptr) {
880 cache->SetInteger(num);
881 }
882 return {true, JSTaggedNumber(num)};
883 }
884 if (num == 0) {
885 return {true, JSTaggedNumber(SignedZero(Sign::NEG))};
886 }
887 num = -num;
888 return {true, JSTaggedNumber(num)};
889 }
890
891 return {false, JSTaggedNumber(NAN_VALUE)};
892 }
893
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)894 double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
895 {
896 auto p = const_cast<uint8_t *>(start);
897 // 1. skip space and line terminal
898 if (!NumberHelper::GotoNonspace(&p, end)) {
899 return 0.0;
900 }
901
902 // 2. get number sign
903 Sign sign = Sign::NONE;
904 if (*p == '+') {
905 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
906 sign = Sign::POS;
907 } else if (*p == '-') {
908 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
909 sign = Sign::NEG;
910 }
911 bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
912
913 // 3. judge Infinity
914 static const char INF[] = "Infinity"; // NOLINT(modernize-avoid-c-arrays)
915 if (*p == INF[0]) {
916 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
917 for (const char *i = &INF[1]; *i != '\0'; ++i) {
918 if (++p == end || *p != *i) {
919 return NAN_VALUE;
920 }
921 }
922 ++p;
923 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
924 return NAN_VALUE;
925 }
926 return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
927 }
928
929 // 4. get number radix
930 bool leadingZero = false;
931 bool prefixRadix = false;
932 if (*p == '0' && radix == 0) {
933 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
934 if (*p == 'x' || *p == 'X') {
935 if ((flags & ALLOW_HEX) == 0) {
936 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
937 }
938 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
939 if (sign != Sign::NONE) {
940 return NAN_VALUE;
941 }
942 prefixRadix = true;
943 radix = HEXADECIMAL;
944 } else if (*p == 'o' || *p == 'O') {
945 if ((flags & ALLOW_OCTAL) == 0) {
946 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
947 }
948 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
949 if (sign != Sign::NONE) {
950 return NAN_VALUE;
951 }
952 prefixRadix = true;
953 radix = OCTAL;
954 } else if (*p == 'b' || *p == 'B') {
955 if ((flags & ALLOW_BINARY) == 0) {
956 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
957 }
958 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
959 if (sign != Sign::NONE) {
960 return NAN_VALUE;
961 }
962 prefixRadix = true;
963 radix = BINARY;
964 } else {
965 leadingZero = true;
966 }
967 }
968
969 if (radix == 0) {
970 radix = DECIMAL;
971 }
972 auto pStart = p;
973 // 5. skip leading '0'
974 while (*p == '0') {
975 RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
976 leadingZero = true;
977 }
978 // 6. parse to number
979 uint64_t intNumber = 0;
980 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
981 int digits = 0;
982 int exponent = 0;
983 do {
984 uint8_t c = ToDigit(*p);
985 if (c >= radix) {
986 if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
987 break;
988 }
989 // "0b" "0x1.2" "0b1e2" ...
990 return NAN_VALUE;
991 }
992 ++digits;
993 if (intNumber < numberMax) {
994 intNumber = intNumber * radix + c;
995 } else {
996 ++exponent;
997 }
998 } while (++p != end);
999
1000 auto number = static_cast<double>(intNumber);
1001 if (sign == Sign::NEG) {
1002 if (number == 0) {
1003 number = -0.0;
1004 } else {
1005 number = -number;
1006 }
1007 }
1008
1009 // 7. deal with other radix except DECIMAL
1010 if (p == end || radix != DECIMAL) {
1011 if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
1012 // no digits there, like "0x", "0xh", or error trailing of "0x3q"
1013 return NAN_VALUE;
1014 }
1015 return number * std::pow(radix, exponent);
1016 }
1017
1018 // 8. parse '.'
1019 exponent = 0;
1020 if (radix == DECIMAL && *p == '.') {
1021 RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
1022 (number * std::pow(radix, exponent)) : NAN_VALUE);
1023 while (ToDigit(*p) < radix) {
1024 --exponent;
1025 ++digits;
1026 if (++p == end) {
1027 break;
1028 }
1029 }
1030 }
1031 if (digits == 0 && !leadingZero) {
1032 // no digits there, like ".", "sss", or ".e1"
1033 return NAN_VALUE;
1034 }
1035 auto pEnd = p;
1036
1037 // 9. parse 'e/E' with '+/-'
1038 char exponentSign = '+';
1039 int additionalExponent = 0;
1040 constexpr int MAX_EXPONENT = INT32_MAX / 2;
1041 if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
1042 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1043
1044 // 10. parse exponent number
1045 if (*p == '+' || *p == '-') {
1046 exponentSign = static_cast<char>(*p);
1047 RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
1048 }
1049 uint8_t digit;
1050 while ((digit = ToDigit(*p)) < radix) {
1051 if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
1052 additionalExponent = MAX_EXPONENT;
1053 } else {
1054 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
1055 }
1056 if (++p == end) {
1057 break;
1058 }
1059 }
1060 }
1061 exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
1062 if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
1063 return NAN_VALUE;
1064 }
1065
1066 // 10. build StringNumericLiteral string
1067 CString buffer;
1068 if (sign == Sign::NEG) {
1069 buffer += "-";
1070 }
1071 for (uint8_t *i = pStart; i < pEnd; ++i) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1072 if (*i != static_cast<uint8_t>('.')) {
1073 buffer += *i;
1074 }
1075 }
1076
1077 // 11. convert none-prefix radix string
1078 return Strtod(buffer.c_str(), exponent, radix);
1079 }
1080
Strtod(const char * str,int exponent,uint8_t radix)1081 double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
1082 {
1083 ASSERT(str != nullptr);
1084 ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
1085 auto p = const_cast<char *>(str);
1086 Sign sign = Sign::NONE;
1087 uint64_t number = 0;
1088 uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
1089 double result = 0.0;
1090 if (*p == '-') {
1091 sign = Sign::NEG;
1092 ++p;
1093 }
1094 while (*p == '0') {
1095 ++p;
1096 }
1097 while (*p != '\0') {
1098 uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
1099 if (digit >= radix) {
1100 break;
1101 }
1102 if (number < numberMax) {
1103 number = number * radix + digit;
1104 } else {
1105 ++exponent;
1106 }
1107 ++p;
1108 }
1109
1110 // cal pow
1111 int exponentAbs = exponent < 0 ? -exponent : exponent;
1112 double powVal = ((radix == DECIMAL) && (exponentAbs < POWERS_OF_TEN_SIZE)) ?
1113 POWERS_OF_TEN[exponentAbs] : std::pow(radix, exponentAbs);
1114 if (exponent < 0) {
1115 result = number / powVal;
1116 } else {
1117 result = number * powVal;
1118 }
1119 return sign == Sign::NEG ? -result : result;
1120 }
1121
DoubleToInt(double d,size_t bits)1122 int32_t NumberHelper::DoubleToInt(double d, size_t bits)
1123 {
1124 int32_t ret = 0;
1125 auto u64 = bit_cast<uint64_t>(d);
1126 int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
1127 if (exp < static_cast<int>(bits - 1)) {
1128 // smaller than INT<bits>_MAX, fast conversion
1129 ret = static_cast<int32_t>(d);
1130 } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
1131 // Still has significand bits after mod 2^<bits>
1132 // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
1133 uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
1134 << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
1135 (INT64_BITS - bits);
1136 ret = static_cast<int32_t>(value);
1137 if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
1138 ret = -ret;
1139 }
1140 } else {
1141 // No significand bits after mod 2^<bits>, contains NaN and INF
1142 ret = 0;
1143 }
1144 return ret;
1145 }
1146
DoubleInRangeInt32(double d)1147 int32_t NumberHelper::DoubleInRangeInt32(double d)
1148 {
1149 if (d >= static_cast<double>(INT_MAX)) {
1150 return INT_MAX;
1151 }
1152 if (d <= static_cast<double>(INT_MIN)) {
1153 return INT_MIN;
1154 }
1155 return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
1156 }
1157
SaturateTruncDoubleToInt32(double d)1158 int32_t NumberHelper::SaturateTruncDoubleToInt32(double d)
1159 {
1160 if (std::isnan(d) || d == -base::POSITIVE_INFINITY) {
1161 return 0;
1162 }
1163 return base::NumberHelper::DoubleInRangeInt32(d);
1164 }
1165
StringToBigInt(JSThread * thread,JSHandle<JSTaggedValue> strVal)1166 JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
1167 {
1168 auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
1169 uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
1170 if (strLen == 0) {
1171 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1172 }
1173 CVector<uint8_t> buf;
1174 Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(thread, buf);
1175
1176 auto p = const_cast<uint8_t *>(str.begin());
1177 auto end = str.end();
1178 // 1. skip space and line terminal
1179 if (!NumberHelper::GotoNonspace(&p, end)) {
1180 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1181 }
1182 // 2. get bigint sign
1183 Sign sign = Sign::NONE;
1184 if (*p == '+') {
1185 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1186 sign = Sign::POS;
1187 } else if (*p == '-') {
1188 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1189 sign = Sign::NEG;
1190 }
1191 // 3. bigint not allow Infinity, decimal points, or exponents.
1192 if (isalpha(*p)) {
1193 return JSTaggedValue(NAN_VALUE);
1194 }
1195 // 4. get bigint radix
1196 uint8_t radix = DECIMAL;
1197 if (*p == '0') {
1198 if (++p == end) {
1199 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1200 }
1201 if (*p == 'x' || *p == 'X') {
1202 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1203 if (sign != Sign::NONE) {
1204 return JSTaggedValue(NAN_VALUE);
1205 }
1206 radix = HEXADECIMAL;
1207 } else if (*p == 'o' || *p == 'O') {
1208 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1209 if (sign != Sign::NONE) {
1210 return JSTaggedValue(NAN_VALUE);
1211 }
1212 radix = OCTAL;
1213 } else if (*p == 'b' || *p == 'B') {
1214 RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
1215 if (sign != Sign::NONE) {
1216 return JSTaggedValue(NAN_VALUE);
1217 }
1218 radix = BINARY;
1219 }
1220 }
1221
1222 // 5. skip leading '0'
1223 while (*p == '0') {
1224 if (++p == end) {
1225 return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
1226 }
1227 }
1228 // 6. parse to bigint
1229 CString buffer;
1230 do {
1231 uint8_t c = ToDigit(*p);
1232 if (c < radix) {
1233 buffer += *p;
1234 } else if (NumberHelper::GotoNonspace(&p, end)) {
1235 // illegal character
1236 return JSTaggedValue(NAN_VALUE);
1237 }
1238 // tail of string is space
1239 } while (++p < end);
1240 if (buffer.size() == 0) {
1241 return BigInt::Uint32ToBigInt(thread, 0).GetTaggedValue();
1242 }
1243 if (sign == Sign::NEG) {
1244 return BigIntHelper::SetBigInt(thread, "-" + buffer, radix).GetTaggedValue();
1245 }
1246 return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
1247 }
1248
GetBase(double d,int digits,int * decimalPoint,char * buf,char * bufTmp,int size)1249 void NumberHelper::GetBase(double d, int digits, int *decimalPoint, char *buf, char *bufTmp, int size)
1250 {
1251 int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
1252 if (result == -1) {
1253 LOG_FULL(FATAL) << "snprintf_s failed";
1254 UNREACHABLE();
1255 }
1256 // mantissa
1257 buf[0] = bufTmp[1];
1258 if (digits > 1) {
1259 if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
1260 LOG_FULL(FATAL) << "memcpy_s failed";
1261 UNREACHABLE();
1262 }
1263 }
1264 buf[digits + 1] = '\0';
1265 // exponent
1266 *decimalPoint = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
1267 }
1268
GetMinmumDigits(double d,int * decimalPoint,char * buf)1269 int NumberHelper::GetMinmumDigits(double d, int *decimalPoint, char *buf)
1270 {
1271 int digits = 0;
1272 char bufTmp[JS_DTOA_BUF_SIZE] = {0};
1273
1274 // find the minimum amount of digits
1275 int MinDigits = 1;
1276 int MaxDigits = DOUBLE_MAX_PRECISION;
1277 while (MinDigits < MaxDigits) {
1278 digits = (MinDigits + MaxDigits) / 2; // 2 : Divide by 2
1279 GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1280 if (strtod(bufTmp, NULL) == d) {
1281 // no need to keep the trailing zeros
1282 while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
1283 digits--;
1284 }
1285 MaxDigits = digits;
1286 } else {
1287 MinDigits = digits + 1;
1288 }
1289 }
1290 digits = MaxDigits;
1291 GetBase(d, digits, decimalPoint, buf, bufTmp, sizeof(bufTmp));
1292
1293 return digits;
1294 }
1295
StringToInt64(const std::string & str,int64_t & value)1296 bool NumberHelper::StringToInt64(const std::string& str, int64_t& value)
1297 {
1298 if (str.empty()) {
1299 return false;
1300 }
1301
1302 char *end;
1303 errno = 0;
1304 value = std::strtoll(str.c_str(), &end, 0); // Automatic check of the number system
1305
1306 // If no number is converted
1307 if (end == str.c_str()) {
1308 return false;
1309 }
1310 // If there is a range error (too large or to small)
1311 if (errno == ERANGE && (value == LLONG_MAX || value == LLONG_MIN)) {
1312 return false;
1313 }
1314 // If the character string contains non-digit chaaracters
1315 if (*end != '\0') {
1316 return false;
1317 }
1318
1319 return true;
1320 }
1321
XorShift64(uint64_t * pVal)1322 uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
1323 {
1324 uint64_t x = *pVal;
1325 x ^= x >> RIGHT12;
1326 x ^= x << LEFT25;
1327 x ^= x >> RIGHT27;
1328 *pVal = x;
1329 return x * GET_MULTIPLY;
1330 }
1331
InitRandom(JSThread * thread)1332 void RandomGenerator::InitRandom(JSThread *thread)
1333 {
1334 struct timeval tv;
1335 gettimeofday(&tv, NULL);
1336 randomState_ = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
1337 // the state must be non zero
1338 if (randomState_ == 0) {
1339 randomState_ = 1;
1340 }
1341 thread->SetRandomStatePtr(&randomState_);
1342 }
1343
NextDouble()1344 double RandomGenerator::NextDouble()
1345 {
1346 uint64_t val = XorShift64(&randomState_);
1347 return ToDouble(val);
1348 }
1349
ToDouble(uint64_t state)1350 double RandomGenerator::ToDouble(uint64_t state)
1351 {
1352 uint64_t random = (state >> base::RIGHT12) | EXPONENTBITS_RANGE_IN_ONE_AND_TWO;
1353 return base::bit_cast<double>(random) - 1;
1354 }
1355
Next(int bits)1356 int32_t RandomGenerator::Next(int bits)
1357 {
1358 uint64_t val = XorShift64(&randomState_);
1359 return static_cast<int32_t>(val >> (INT64_BITS - bits));
1360 }
1361
GenerateIdentityHash()1362 int32_t RandomGenerator::GenerateIdentityHash()
1363 {
1364 return RandomGenerator::Next(INT32_BITS) & INT32_MAX;
1365 }
1366 } // namespace panda::ecmascript::base
1367