• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 <cmath>
19 #include <cstddef>
20 #include <cstdint>
21 #include <iomanip>
22 #include <sstream>
23 #include <sys/time.h>
24 
25 #include "ecmascript/base/builtins_base.h"
26 #include "ecmascript/base/string_helper.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/object_factory.h"
29 
30 namespace panda::ecmascript::base {
31 enum class Sign { NONE, NEG, POS };
32 thread_local uint64_t RandomGenerator::randomState {0};
33 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
34 #define RETURN_IF_CONVERSION_END(p, end, result) \
35     if ((p) == (end)) {                          \
36         return (result);                         \
37     }
38 
39 constexpr char CHARS[] = "0123456789abcdefghijklmnopqrstuvwxyz";  // NOLINT (modernize-avoid-c-arrays)
40 constexpr uint64_t MAX_MANTISSA = 0x1ULL << 52U;
41 
ToDigit(uint8_t c)42 static inline uint8_t ToDigit(uint8_t c)
43 {
44     if (c >= '0' && c <= '9') {
45         return c - '0';
46     }
47     if (c >= 'A' && c <= 'Z') {
48         return c - 'A' + DECIMAL;
49     }
50     if (c >= 'a' && c <= 'z') {
51         return c - 'a' + DECIMAL;
52     }
53     return '$';
54 }
55 
GotoNonspace(uint8_t ** ptr,const uint8_t * end)56 bool NumberHelper::GotoNonspace(uint8_t **ptr, const uint8_t *end)
57 {
58     while (*ptr < end) {
59         uint16_t c = **ptr;
60         size_t size = 1;
61         if (c > INT8_MAX) {
62             size = 0;
63             uint16_t utf8Bit = INT8_MAX + 1;  // equal 0b1000'0000
64             while (utf8Bit > 0 && (c & utf8Bit) == utf8Bit) {
65                 ++size;
66                 utf8Bit >>= 1UL;
67             }
68             if (base::utf_helper::ConvertRegionUtf8ToUtf16(*ptr, &c, end - *ptr, 1, 0) <= 0) {
69                 return true;
70             }
71         }
72         if (!StringHelper::IsNonspace(c)) {
73             return true;
74         }
75         *ptr += size;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
76     }
77     return false;
78 }
79 
SignedZero(Sign sign)80 static inline double SignedZero(Sign sign)
81 {
82     return sign == Sign::NEG ? -0.0 : 0.0;
83 }
84 
IsEmptyString(const uint8_t * start,const uint8_t * end)85 bool NumberHelper::IsEmptyString(const uint8_t *start, const uint8_t *end)
86 {
87     auto p = const_cast<uint8_t *>(start);
88     return !NumberHelper::GotoNonspace(&p, end);
89 }
90 
DoubleToString(JSThread * thread,double number,int radix)91 JSTaggedValue NumberHelper::DoubleToString(JSThread *thread, double number, int radix)
92 {
93     bool negative = false;
94     if (number < 0.0) {
95         negative = true;
96         number = -number;
97     }
98 
99     int64_t numberInteger = static_cast<int64_t>(std::floor(number));
100     double numberFraction = number - numberInteger;
101 
102     auto value = bit_cast<uint64_t>(number);
103     value += 1;
104     double delta = HALF * (bit_cast<double>(value) - number);
105 
106     CString result;
107     if (numberFraction != 0 && numberFraction >= delta) {
108         result += ".";
109         result += DecimalsToString(&numberInteger, numberFraction, radix, delta);
110     }
111 
112     result = IntegerToString(numberInteger, radix) + result;
113 
114     if (negative) {
115         result = "-" + result;
116     }
117 
118     return BuiltinsBase::GetTaggedString(thread, result.c_str());
119 }
120 
DoubleToExponential(JSThread * thread,double number,int digit)121 JSTaggedValue NumberHelper::DoubleToExponential(JSThread *thread, double number, int digit)
122 {
123     CStringStream ss;
124     if (digit < 0) {
125         ss << std::setiosflags(std::ios::scientific) << std::setprecision(base::MAX_PRECISION) << number;
126     } else {
127         ss << std::setiosflags(std::ios::scientific) << std::setprecision(digit) << number;
128     }
129     CString result = ss.str();
130     size_t found = result.find_last_of('e');
131     if (found != CString::npos && found < result.size() - 2 && result[found + 2] == '0') {
132         result.erase(found + 2, 1); // 2:offset of e
133     }
134     if (digit < 0) {
135         size_t end = found;
136         while (--found > 0) {
137             if (result[found] != '0') {
138                 break;
139             }
140         }
141         if (result[found] == '.') {
142             found--;
143         }
144         if (found < end - 1) {
145             result.erase(found + 1, end - found - 1);
146         }
147     }
148     return BuiltinsBase::GetTaggedString(thread, result.c_str());
149 }
150 
DoubleToFixed(JSThread * thread,double number,int digit)151 JSTaggedValue NumberHelper::DoubleToFixed(JSThread *thread, double number, int digit)
152 {
153     CStringStream ss;
154     ss << std::setiosflags(std::ios::fixed) << std::setprecision(digit) << number;
155     return BuiltinsBase::GetTaggedString(thread, ss.str().c_str());
156 }
157 
DoubleToPrecision(JSThread * thread,double number,int digit)158 JSTaggedValue NumberHelper::DoubleToPrecision(JSThread *thread, double number, int digit)
159 {
160     if (number == 0.0) {
161         return DoubleToFixed(thread, number, digit - 1);
162     }
163     CStringStream ss;
164     double positiveNumber = number > 0 ? number : -number;
165     int logDigit = std::floor(log10(positiveNumber));
166     int radixDigit = digit - logDigit - 1;
167     const int MAX_EXPONENT_DIGIT = 6;
168     if ((logDigit >= 0 && radixDigit >= 0) || (logDigit < 0 && radixDigit <= MAX_EXPONENT_DIGIT)) {
169         return DoubleToFixed(thread, number, std::abs(radixDigit));
170     }
171     return DoubleToExponential(thread, number, digit - 1);
172 }
173 
StringToDoubleWithRadix(const uint8_t * start,const uint8_t * end,int radix)174 JSTaggedValue NumberHelper::StringToDoubleWithRadix(const uint8_t *start, const uint8_t *end, int radix)
175 {
176     auto p = const_cast<uint8_t *>(start);
177     JSTaggedValue nanResult = BuiltinsBase::GetTaggedDouble(NAN_VALUE);
178     // 1. skip space and line terminal
179     if (!NumberHelper::GotoNonspace(&p, end)) {
180         return nanResult;
181     }
182 
183     // 2. sign bit
184     bool negative = false;
185     if (*p == '-') {
186         negative = true;
187         RETURN_IF_CONVERSION_END(++p, end, nanResult);
188     } else if (*p == '+') {
189         RETURN_IF_CONVERSION_END(++p, end, nanResult);
190     }
191     // 3. 0x or 0X
192     bool stripPrefix = true;
193     // 4. If R  0, then
194     //     a. If R < 2 or R > 36, return NaN.
195     //     b. If R  16, let stripPrefix be false.
196     if (radix != 0) {
197         if (radix < MIN_RADIX || radix > MAX_RADIX) {
198             return nanResult;
199         }
200         if (radix != HEXADECIMAL) {
201             stripPrefix = false;
202         }
203     } else {
204         radix = DECIMAL;
205     }
206     int size = 0;
207     if (stripPrefix) {
208         if (*p == '0') {
209             size++;
210             if (++p != end && (*p == 'x' || *p == 'X')) {
211                 RETURN_IF_CONVERSION_END(++p, end, nanResult);
212                 radix = HEXADECIMAL;
213             }
214         }
215     }
216 
217     double result = 0;
218     bool isDone = false;
219     do {
220         double part = 0;
221         uint32_t multiplier = 1;
222         for (; p != end; ++p) {
223             // The maximum value to ensure that uint32_t will not overflow
224             const uint32_t MAX_MULTIPER = 0xffffffffU / 36;
225             uint32_t m = multiplier * static_cast<uint32_t>(radix);
226             if (m > MAX_MULTIPER) {
227                 break;
228             }
229 
230             int currentBit = static_cast<int>(ToDigit(*p));
231             if (currentBit >= radix) {
232                 isDone = true;
233                 break;
234             }
235             size++;
236             part = part * radix + currentBit;
237             multiplier = m;
238         }
239         result = result * multiplier + part;
240         if (isDone) {
241             break;
242         }
243     } while (p != end);
244 
245     if (size == 0) {
246         return nanResult;
247     }
248 
249     if (negative) {
250         result = -result;
251     }
252     return BuiltinsBase::GetTaggedDouble(result);
253 }
254 
Carry(char current,int radix)255 char NumberHelper::Carry(char current, int radix)
256 {
257     int digit = static_cast<int>((current > '9') ? (current - 'a' + DECIMAL) : (current - '0'));
258     digit = (digit == (radix - 1)) ? 0 : digit + 1;
259     return CHARS[digit];
260 }
261 
IntegerToString(int64_t number,int radix)262 CString NumberHelper::IntegerToString(int64_t number, int radix)
263 {
264     ASSERT(radix >= MIN_RADIX && radix <= MAX_RADIX);
265     CString result;
266     while (number / radix > static_cast<int64_t>(MAX_MANTISSA)) {
267         number /= radix;
268         result = CString("0").append(result);
269     }
270     do {
271         uint8_t remainder = static_cast<uint8_t>(std::fmod(number, radix));
272         result = CHARS[remainder] + result;
273         number = (number - remainder) / radix;
274     } while (number > 0);
275     return result;
276 }
277 
DecimalsToString(int64_t * numberInteger,double fraction,int radix,double delta)278 CString NumberHelper::DecimalsToString(int64_t *numberInteger, double fraction, int radix, double delta)
279 {
280     CString result;
281     while (fraction >= delta) {
282         fraction *= radix;
283         delta *= radix;
284         int64_t integer = std::floor(fraction);
285         fraction -= integer;
286         result += CHARS[integer];
287         if (fraction > HALF && fraction + delta > 1) {
288             size_t fractionEnd = result.size() - 1;
289             result[fractionEnd] = Carry(*result.rbegin(), radix);
290             for (; fractionEnd > 0; fractionEnd--) {
291                 if (result[fractionEnd] == '0') {
292                     result[fractionEnd - 1] = Carry(result[fractionEnd - 1], radix);
293                 } else {
294                     break;
295                 }
296             }
297             if (fractionEnd == 0) {
298                 (*numberInteger)++;
299             }
300             break;
301         }
302     }
303     // delete 0 in the end
304     size_t found = result.find_last_not_of('0');
305     if (found != CString::npos) {
306         result.erase(found + 1);
307     }
308 
309     return result;
310 }
311 
IntToString(int number)312 CString NumberHelper::IntToString(int number)
313 {
314     return ToCString(number);
315 }
316 
317 // 7.1.12.1 ToString Applied to the Number Type
NumberToString(const JSThread * thread,JSTaggedValue number)318 JSHandle<EcmaString> NumberHelper::NumberToString(const JSThread *thread, JSTaggedValue number)
319 {
320     ASSERT(number.IsNumber());
321     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
322     if (number.IsInt()) {
323         return factory->NewFromASCII(IntToString(number.GetInt()));
324     }
325 
326     double d = number.GetDouble();
327     if (std::isnan(d)) {
328         return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledNanCapitalString());
329     }
330     if (d == 0.0) {
331         return JSHandle<EcmaString>::Cast(thread->GlobalConstants()->GetHandledZeroString());
332     }
333     if (d >= INT32_MIN + 1 && d <= INT32_MAX && d == static_cast<double>(static_cast<int32_t>(d))) {
334         return factory->NewFromASCII(IntToString(static_cast<int32_t>(d)));
335     }
336 
337     std::string result;
338     if (d < 0) {
339         result += "-";
340         d = -d;
341     }
342 
343     if (std::isinf(d)) {
344         result += "Infinity";
345         return factory->NewFromASCII(result.c_str());
346     }
347 
348     ASSERT(d > 0);
349 
350     // 5. Otherwise, let n, k, and s be integers such that k ≥ 1, 10k−1 ≤ s < 10k, the Number value for s × 10n−k is m,
351     // and k is as small as possible. If there are multiple possibilities for s, choose the value of s for which s ×
352     // 10n−k is closest in value to m. If there are two such possible values of s, choose the one that is even. Note
353     // that k is the number of digits in the decimal representation of s and that s is not divisible by 10.
354     if (0.1 <= d && d < 1) {  // 0.1: 10 ** -1
355         // Fast path. In this case, n==0, just need to calculate k and s.
356         std::string resultFast = "0.";
357         int64_t sFast = 0;
358         int kFast = 1;
359         int64_t power = 1;
360         while (kFast <= DOUBLE_MAX_PRECISION) {
361             power *= 10;  // 10: base 10
362             int digitFast = static_cast<int64_t>(d * power) % 10;  // 10: base 10
363             ASSERT(0 <= digitFast && digitFast <= 9);  // 9: single digit max
364             sFast = sFast * 10 + digitFast;  // 10: base 10
365             resultFast += (digitFast + '0');
366             if (sFast / static_cast<double>(power) == d) {  // s * (10 ** -k)
367                 break;
368             }
369             kFast++;
370         }
371         result += resultFast;
372         return factory->NewFromASCII(result.c_str());
373     }
374     char buffer[JS_DTOA_BUF_SIZE] = {0};
375     int n = 0;
376     int k = GetMinmumDigits(d, &n, buffer);
377     std::string base = buffer;
378     if (n > 0 && n <= 21) {  // NOLINT(readability-magic-numbers)
379         base.erase(1, 1);
380         if (k <= n) {
381             // 6. If k ≤ n ≤ 21, return the String consisting of the code units of the k digits of the decimal
382             // representation of s (in order, with no leading zeroes), followed by n−k occurrences of the code unit
383             // 0x0030 (DIGIT ZERO).
384             base += std::string(n - k, '0');
385         } else {
386             // 7. If 0 < n ≤ 21, return the String consisting of the code units of the most significant n digits of the
387             // decimal representation of s, followed by the code unit 0x002E (FULL STOP), followed by the code units of
388             // the remaining k−n digits of the decimal representation of s.
389             base.insert(n, 1, '.');
390         }
391     } else if (-6 < n && n <= 0) {  // NOLINT(readability-magic-numbers)
392         // 8. If −6 < n ≤ 0, return the String consisting of the code unit 0x0030 (DIGIT ZERO), followed by the code
393         // unit 0x002E (FULL STOP), followed by −n occurrences of the code unit 0x0030 (DIGIT ZERO), followed by the
394         // code units of the k digits of the decimal representation of s.
395         base.erase(1, 1);
396         base = std::string("0.") + std::string(-n, '0') + base;
397     } else {
398         if (k == 1) {
399             // 9. Otherwise, if k = 1, return the String consisting of the code unit of the single digit of s
400             base.erase(1, 1);
401         }
402         // followed by code unit 0x0065 (LATIN SMALL LETTER E), followed by the code unit 0x002B (PLUS SIGN) or the code
403         // unit 0x002D (HYPHEN-MINUS) according to whether n−1 is positive or negative, followed by the code units of
404         // the decimal representation of the integer abs(n−1) (with no leading zeroes).
405         base += "e" + (n >= 1 ? std::string("+") : "") + std::to_string(n - 1);
406     }
407     result += base;
408     return factory->NewFromASCII(result.c_str());
409 }
410 
TruncateDouble(double d)411 double NumberHelper::TruncateDouble(double d)
412 {
413     if (std::isnan(d)) {
414         return 0;
415     }
416     if (!std::isfinite(d)) {
417         return d;
418     }
419     // -0 to +0
420     if (d == 0.0) {
421         return 0;
422     }
423     return (d >= 0) ? std::floor(d) : std::ceil(d);
424 }
425 
DoubleToInt64(double d)426 int64_t NumberHelper::DoubleToInt64(double d)
427 {
428     if (d >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
429         return std::numeric_limits<int64_t>::max();
430     }
431     if (d <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
432         return std::numeric_limits<int64_t>::min();
433     }
434     return static_cast<int64_t>(d);
435 }
436 
StringToDouble(const uint8_t * start,const uint8_t * end,uint8_t radix,uint32_t flags)437 double NumberHelper::StringToDouble(const uint8_t *start, const uint8_t *end, uint8_t radix, uint32_t flags)
438 {
439     auto p = const_cast<uint8_t *>(start);
440     // 1. skip space and line terminal
441     if (!NumberHelper::GotoNonspace(&p, end)) {
442         return 0.0;
443     }
444 
445     // 2. get number sign
446     Sign sign = Sign::NONE;
447     if (*p == '+') {
448         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
449         sign = Sign::POS;
450     } else if (*p == '-') {
451         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
452         sign = Sign::NEG;
453     }
454     bool ignoreTrailing = (flags & IGNORE_TRAILING) != 0;
455 
456     // 3. judge Infinity
457     static const char INF[] = "Infinity";  // NOLINT(modernize-avoid-c-arrays)
458     if (*p == INF[0]) {
459         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
460         for (const char *i = &INF[1]; *i != '\0'; ++i) {
461             if (++p == end || *p != *i) {
462                 return NAN_VALUE;
463             }
464         }
465         ++p;
466         if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
467             return NAN_VALUE;
468         }
469         return sign == Sign::NEG ? -POSITIVE_INFINITY : POSITIVE_INFINITY;
470     }
471 
472     // 4. get number radix
473     bool leadingZero = false;
474     bool prefixRadix = false;
475     if (*p == '0' && radix == 0) {
476         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
477         if (*p == 'x' || *p == 'X') {
478             if ((flags & ALLOW_HEX) == 0) {
479                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
480             }
481             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
482             if (sign != Sign::NONE) {
483                 return NAN_VALUE;
484             }
485             prefixRadix = true;
486             radix = HEXADECIMAL;
487         } else if (*p == 'o' || *p == 'O') {
488             if ((flags & ALLOW_OCTAL) == 0) {
489                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
490             }
491             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
492             if (sign != Sign::NONE) {
493                 return NAN_VALUE;
494             }
495             prefixRadix = true;
496             radix = OCTAL;
497         } else if (*p == 'b' || *p == 'B') {
498             if ((flags & ALLOW_BINARY) == 0) {
499                 return ignoreTrailing ? SignedZero(sign) : NAN_VALUE;
500             }
501             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
502             if (sign != Sign::NONE) {
503                 return NAN_VALUE;
504             }
505             prefixRadix = true;
506             radix = BINARY;
507         } else {
508             leadingZero = true;
509         }
510     }
511 
512     if (radix == 0) {
513         radix = DECIMAL;
514     }
515     auto pStart = p;
516     // 5. skip leading '0'
517     while (*p == '0') {
518         RETURN_IF_CONVERSION_END(++p, end, SignedZero(sign));
519         leadingZero = true;
520     }
521     // 6. parse to number
522     uint64_t intNumber = 0;
523     uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
524     int digits = 0;
525     int exponent = 0;
526     do {
527         uint8_t c = ToDigit(*p);
528         if (c >= radix) {
529             if (!prefixRadix || ignoreTrailing || (pStart != p && !NumberHelper::GotoNonspace(&p, end))) {
530                 break;
531             }
532             // "0b" "0x1.2" "0b1e2" ...
533             return NAN_VALUE;
534         }
535         ++digits;
536         if (intNumber < numberMax) {
537             intNumber = intNumber * radix + c;
538         } else {
539             ++exponent;
540         }
541     } while (++p != end);
542 
543     auto number = static_cast<double>(intNumber);
544     if (sign == Sign::NEG) {
545         if (number == 0) {
546             number = -0.0;
547         } else {
548             number = -number;
549         }
550     }
551 
552     // 7. deal with other radix except DECIMAL
553     if (p == end || radix != DECIMAL) {
554         if ((digits == 0 && !leadingZero) || (p != end && !ignoreTrailing && NumberHelper::GotoNonspace(&p, end))) {
555             // no digits there, like "0x", "0xh", or error trailing of "0x3q"
556             return NAN_VALUE;
557         }
558         return number * std::pow(radix, exponent);
559     }
560 
561     // 8. parse '.'
562     if (radix == DECIMAL && *p == '.') {
563         RETURN_IF_CONVERSION_END(++p, end, (digits > 0 || (digits == 0 && leadingZero)) ?
564                                            (number * std::pow(radix, exponent)) : NAN_VALUE);
565         while (ToDigit(*p) < radix) {
566             --exponent;
567             ++digits;
568             if (++p == end) {
569                 break;
570             }
571         }
572     }
573     if (digits == 0 && !leadingZero) {
574         // no digits there, like ".", "sss", or ".e1"
575         return NAN_VALUE;
576     }
577     auto pEnd = p;
578 
579     // 9. parse 'e/E' with '+/-'
580     char exponentSign = '+';
581     int additionalExponent = 0;
582     constexpr int MAX_EXPONENT = INT32_MAX / 2;
583     if (radix == DECIMAL && (p != end && (*p == 'e' || *p == 'E'))) {
584         RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
585 
586         // 10. parse exponent number
587         if (*p == '+' || *p == '-') {
588             exponentSign = static_cast<char>(*p);
589             RETURN_IF_CONVERSION_END(++p, end, NAN_VALUE);
590         }
591         uint8_t digit;
592         while ((digit = ToDigit(*p)) < radix) {
593             if (additionalExponent > static_cast<int>(MAX_EXPONENT / radix)) {
594                 additionalExponent = MAX_EXPONENT;
595             } else {
596                 additionalExponent = additionalExponent * static_cast<int>(radix) + static_cast<int>(digit);
597             }
598             if (++p == end) {
599                 break;
600             }
601         }
602     }
603     exponent += (exponentSign == '-' ? -additionalExponent : additionalExponent);
604     if (!ignoreTrailing && NumberHelper::GotoNonspace(&p, end)) {
605         return NAN_VALUE;
606     }
607 
608     // 10. build StringNumericLiteral string
609     CString buffer;
610     if (sign == Sign::NEG) {
611         buffer += "-";
612     }
613     for (uint8_t *i = pStart; i < pEnd; ++i) {  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
614         if (*i != static_cast<uint8_t>('.')) {
615             buffer += *i;
616         }
617     }
618 
619     // 11. convert none-prefix radix string
620     return Strtod(buffer.c_str(), exponent, radix);
621 }
622 
Strtod(const char * str,int exponent,uint8_t radix)623 double NumberHelper::Strtod(const char *str, int exponent, uint8_t radix)
624 {
625     ASSERT(str != nullptr);
626     ASSERT(radix >= base::MIN_RADIX && radix <= base::MAX_RADIX);
627     auto p = const_cast<char *>(str);
628     Sign sign = Sign::NONE;
629     uint64_t number = 0;
630     uint64_t numberMax = (UINT64_MAX - (radix - 1)) / radix;
631     double result = 0.0;
632     if (*p == '-') {
633         sign = Sign::NEG;
634         ++p;
635     }
636     while (*p == '0') {
637         ++p;
638     }
639     while (*p != '\0') {
640         uint8_t digit = ToDigit(static_cast<uint8_t>(*p));
641         if (digit >= radix) {
642             break;
643         }
644         if (number < numberMax) {
645             number = number * radix + digit;
646         } else {
647             ++exponent;
648         }
649         ++p;
650     }
651     if (exponent < 0) {
652         result = number / std::pow(radix, -exponent);
653     } else {
654         result = number * std::pow(radix, exponent);
655     }
656     return sign == Sign::NEG ? -result : result;
657 }
658 
DoubleToInt(double d,size_t bits)659 int32_t NumberHelper::DoubleToInt(double d, size_t bits)
660 {
661     int32_t ret = 0;
662     auto u64 = bit_cast<uint64_t>(d);
663     int exp = static_cast<int>((u64 & DOUBLE_EXPONENT_MASK) >> DOUBLE_SIGNIFICAND_SIZE) - DOUBLE_EXPONENT_BIAS;
664     if (exp < static_cast<int>(bits - 1)) {
665         // smaller than INT<bits>_MAX, fast conversion
666         ret = static_cast<int32_t>(d);
667     } else if (exp < static_cast<int>(bits + DOUBLE_SIGNIFICAND_SIZE)) {
668         // Still has significand bits after mod 2^<bits>
669         // Get low <bits> bits by shift left <64 - bits> and shift right <64 - bits>
670         uint64_t value = (((u64 & DOUBLE_SIGNIFICAND_MASK) | DOUBLE_HIDDEN_BIT)
671                           << (static_cast<uint32_t>(exp) - DOUBLE_SIGNIFICAND_SIZE + INT64_BITS - bits)) >>
672                          (INT64_BITS - bits);
673         ret = static_cast<int32_t>(value);
674         if ((u64 & DOUBLE_SIGN_MASK) == DOUBLE_SIGN_MASK && ret != INT32_MIN) {
675             ret = -ret;
676         }
677     } else {
678         // No significand bits after mod 2^<bits>, contains NaN and INF
679         ret = 0;
680     }
681     return ret;
682 }
683 
DoubleInRangeInt32(double d)684 int32_t NumberHelper::DoubleInRangeInt32(double d)
685 {
686     if (d > INT_MAX) {
687         return INT_MAX;
688     }
689     if (d < INT_MIN) {
690         return INT_MIN;
691     }
692     return base::NumberHelper::DoubleToInt(d, base::INT32_BITS);
693 }
694 
StringToBigInt(JSThread * thread,JSHandle<JSTaggedValue> strVal)695 JSTaggedValue NumberHelper::StringToBigInt(JSThread *thread, JSHandle<JSTaggedValue> strVal)
696 {
697     auto strObj = static_cast<EcmaString *>(strVal->GetTaggedObject());
698     uint32_t strLen = EcmaStringAccessor(strObj).GetLength();
699     if (strLen == 0) {
700         return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
701     }
702     [[maybe_unused]] CVector<uint8_t> buf;
703     Span<const uint8_t> str = EcmaStringAccessor(strObj).ToUtf8Span(buf);
704 
705     auto p = const_cast<uint8_t *>(str.begin());
706     auto end = str.end();
707     // 1. skip space and line terminal
708     if (!NumberHelper::GotoNonspace(&p, end)) {
709         return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
710     }
711     // 2. get bigint sign
712     Sign sign = Sign::NONE;
713     if (*p == '+') {
714         RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
715         sign = Sign::POS;
716     } else if (*p == '-') {
717         RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
718         sign = Sign::NEG;
719     }
720     // 3. bigint not allow Infinity, decimal points, or exponents.
721     if (isalpha(*p)) {
722         return JSTaggedValue(NAN_VALUE);
723     }
724     // 4. get bigint radix
725     uint8_t radix = DECIMAL;
726     if (*p == '0') {
727         if (++p == end) {
728             return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
729         }
730         if (*p == 'x' || *p == 'X') {
731             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
732             if (sign != Sign::NONE) {
733                 return JSTaggedValue(NAN_VALUE);
734             }
735             radix = HEXADECIMAL;
736         } else if (*p == 'o' || *p == 'O') {
737             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
738             if (sign != Sign::NONE) {
739                 return JSTaggedValue(NAN_VALUE);
740             }
741             radix = OCTAL;
742         } else if (*p == 'b' || *p == 'B') {
743             RETURN_IF_CONVERSION_END(++p, end, JSTaggedValue(NAN_VALUE));
744             if (sign != Sign::NONE) {
745                 return JSTaggedValue(NAN_VALUE);
746             }
747             radix = BINARY;
748         }
749     }
750 
751     auto pStart = p;
752     // 5. skip leading '0'
753     while (*p == '0') {
754         if (++p == end) {
755             return BigInt::Int32ToBigInt(thread, 0).GetTaggedValue();
756         }
757     }
758     // 6. parse to bigint
759     CString buffer;
760     if (sign == Sign::NEG) {
761         buffer += "-";
762     }
763     do {
764         uint8_t c = ToDigit(*p);
765         if (c >= radix) {
766             if (pStart != p && !NumberHelper::GotoNonspace(&p, end)) {
767                 break;
768             }
769             return JSTaggedValue(NAN_VALUE);
770         }
771         buffer += *p;
772     } while (++p != end);
773     return BigIntHelper::SetBigInt(thread, buffer, radix).GetTaggedValue();
774 }
775 
GetBase(double d,int digits,int * decpt,char * buf,char * bufTmp,int size)776 void NumberHelper::GetBase(double d, int digits, int *decpt, char *buf, char *bufTmp, int size)
777 {
778     int result = snprintf_s(bufTmp, size, size - 1, "%+.*e", digits - 1, d);
779     if (result == -1) {
780         LOG_FULL(FATAL) << "snprintf_s failed";
781         UNREACHABLE();
782     }
783     // mantissa
784     buf[0] = bufTmp[1];
785     if (digits > 1) {
786         if (memcpy_s(buf + 1, digits, bufTmp + 2, digits) != EOK) { // 2 means add the point char to buf
787             LOG_FULL(FATAL) << "memcpy_s failed";
788             UNREACHABLE();
789         }
790     }
791     buf[digits + 1] = '\0';
792     // exponent
793     *decpt = atoi(bufTmp + digits + 2 + (digits > 1)) + 1; // 2 means ignore the integer and point
794 }
795 
GetMinmumDigits(double d,int * decpt,char * buf)796 int NumberHelper::GetMinmumDigits(double d, int *decpt, char *buf)
797 {
798     int digits = 0;
799     char bufTmp[JS_DTOA_BUF_SIZE] = {0};
800 
801     // find the minimum amount of digits
802     int MinDigits = 1;
803     int MaxDigits = DOUBLE_MAX_PRECISION;
804     while (MinDigits < MaxDigits) {
805         digits = (MinDigits + MaxDigits) / 2;
806         GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
807         if (strtod(bufTmp, NULL) == d) {
808             // no need to keep the trailing zeros
809             while (digits >= 2 && buf[digits] == '0') { // 2 means ignore the integer and point
810                 digits--;
811             }
812             MaxDigits = digits;
813         } else {
814             MinDigits = digits + 1;
815         }
816     }
817     digits = MaxDigits;
818     GetBase(d, digits, decpt, buf, bufTmp, sizeof(bufTmp));
819 
820     return digits;
821 }
GetRandomState()822 uint64_t& RandomGenerator::GetRandomState()
823 {
824     return randomState;
825 }
XorShift64(uint64_t * pVal)826 uint64_t RandomGenerator::XorShift64(uint64_t *pVal)
827 {
828     uint64_t x = *pVal;
829     x ^= x >> RIGHT12;
830     x ^= x << LEFT25;
831     x ^= x >> RIGHT27;
832     *pVal = x;
833     return x * GET_MULTIPLY;
834 }
InitRandom()835 void RandomGenerator::InitRandom()
836 {
837     struct timeval tv;
838     gettimeofday(&tv, NULL);
839     randomState = static_cast<uint64_t>((tv.tv_sec * SECONDS_TO_SUBTLE) + tv.tv_usec);
840     // the state must be non zero
841     if (randomState == 0)
842         randomState = 1;
843 }
844 }  // namespace panda::ecmascript::base
845