• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/builtins/builtins_number.h"
17 
18 #include "ecmascript/ecma_string-inl.h"
19 #include "ecmascript/js_function.h"
20 #include "ecmascript/js_primitive_ref.h"
21 #ifdef ARK_SUPPORT_INTL
22 #include "ecmascript/js_number_format.h"
23 #else
24 #ifndef ARK_NOT_SUPPORT_INTL_GLOBAL
25 #include "ecmascript/intl/global_intl_helper.h"
26 #endif
27 #endif
28 
29 namespace panda::ecmascript::builtins {
30 using NumberHelper = base::NumberHelper;
31 
NumberConstructor(EcmaRuntimeCallInfo * argv)32 JSTaggedValue BuiltinsNumber::NumberConstructor(EcmaRuntimeCallInfo *argv)
33 {
34     ASSERT(argv);
35     BUILTINS_API_TRACE(argv->GetThread(), Number, Constructor);
36     JSThread *thread = argv->GetThread();
37     [[maybe_unused]] EcmaHandleScope handleScope(thread);
38     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
39 
40     // 1. If value is present, then a , b , c.
41     // 2. Else Let n be +0��.
42     JSTaggedNumber numberValue(0);
43     if (argv->GetArgsNumber() > 0) {
44         JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
45         // a. Let prim be ? ToNumeric(value).
46         if (!value->IsNumber()) {
47             JSHandle<JSTaggedValue> numericVal = JSTaggedValue::ToNumeric(thread, value);
48             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
49             // b. If Type(prim) is BigInt, let n be ��(ℝ(prim)).
50             if (numericVal->IsBigInt()) {
51                 JSHandle<BigInt> bigNumericVal(numericVal);
52                 numberValue = BigInt::BigIntToNumber(bigNumericVal);
53             } else {
54                 // c. Otherwise, let n be prim.
55                 numberValue = JSTaggedNumber(numericVal.GetTaggedValue());
56             }
57         } else {
58             numberValue = JSTaggedNumber(value.GetTaggedValue());
59         }
60     }
61     // 3. If NewTarget is undefined, return n.
62     if (newTarget->IsUndefined()) {
63         return numberValue;
64     }
65     // 4. Let O be OrdinaryCreateFromConstructor(NewTarget, "%NumberPrototype%", «[[NumberData]]» ).
66     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
67     JSHandle<JSObject> result =
68         thread->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(JSHandle<JSFunction>::Cast(constructor), newTarget);
69     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
70     // 5. Set O.[[NumberData]] to n.
71     JSPrimitiveRef::Cast(*result)->SetValue(thread, numberValue);
72     // 6. Return O.
73     return result.GetTaggedValue();
74 }
75 
76 // 20.1.2.2
IsFinite(EcmaRuntimeCallInfo * argv)77 JSTaggedValue BuiltinsNumber::IsFinite(EcmaRuntimeCallInfo *argv)
78 {
79     ASSERT(argv);
80     BUILTINS_API_TRACE(argv->GetThread(), Number, IsFinite);
81     JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue();
82     // 1. If Type(number) is not Number, return false
83     // 2. If number is NaN, +infinite, or -infinite, return false
84     if (NumberHelper::IsFinite(msg)) {
85         return GetTaggedBoolean(true);
86     }
87     return GetTaggedBoolean(false);
88 }
89 
90 // 20.1.2.3
IsInteger(EcmaRuntimeCallInfo * argv)91 JSTaggedValue BuiltinsNumber::IsInteger(EcmaRuntimeCallInfo *argv)
92 {
93     ASSERT(argv);
94     BUILTINS_API_TRACE(argv->GetThread(), Number, IsInteger);
95     JSThread *thread = argv->GetThread();
96     JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
97     bool result = false;
98     // 1. If Type(number) is not Number, return false.
99     // 2. If number is NaN, +infinite, or -infinite, return false
100     if (NumberHelper::IsFinite(msg.GetTaggedValue())) {
101         [[maybe_unused]] EcmaHandleScope handleScope(thread);
102         double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber();
103         // 3. Let integer be ToInteger(number).
104         JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg);
105         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
106         // 4. If integer is not equal to number, return false.
107         // 5. Otherwise, return true.
108         result = (value == number.GetNumber());
109     }
110     return GetTaggedBoolean(result);
111 }
112 
113 // 20.1.2.4
IsNaN(EcmaRuntimeCallInfo * argv)114 JSTaggedValue BuiltinsNumber::IsNaN(EcmaRuntimeCallInfo *argv)
115 {
116     ASSERT(argv);
117     BUILTINS_API_TRACE(argv->GetThread(), Number, IsNaN);
118     JSTaggedValue msg = GetCallArg(argv, 0).GetTaggedValue();
119     // 1. If Type(number) is not Number, return false.
120     // 2. If number is NaN, return true.
121     if (NumberHelper::IsNaN(msg)) {
122         return GetTaggedBoolean(true);
123     }
124     // 3. Otherwise, return false.
125     return GetTaggedBoolean(false);
126 }
127 
128 // 20.1.2.5
IsSafeInteger(EcmaRuntimeCallInfo * argv)129 JSTaggedValue BuiltinsNumber::IsSafeInteger(EcmaRuntimeCallInfo *argv)
130 {
131     ASSERT(argv);
132     BUILTINS_API_TRACE(argv->GetThread(), Number, IsSafeInteger);
133     JSThread *thread = argv->GetThread();
134     JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
135     bool result = false;
136     // 1. If Type(number) is not Number, return false.
137     // 2. If number is NaN, +infinite, or -infinite, return false
138     if (NumberHelper::IsFinite(msg.GetTaggedValue())) {
139         [[maybe_unused]] EcmaHandleScope handleScope(thread);
140         double value = JSTaggedNumber(msg.GetTaggedValue()).GetNumber();
141         // 3. Let integer be ToInteger(number).
142         JSTaggedNumber number = JSTaggedValue::ToInteger(thread, msg);
143         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
144         // 4. If integer is not equal to number, return false.
145         // 5. If abs(integer) ≤ 253−1, return true.
146         result = (value == number.GetNumber()) && std::abs(value) <= base::MAX_SAFE_INTEGER;
147     }
148     return GetTaggedBoolean(result);
149 }
150 
151 // 18.2.4
152 // 20.1.2.12
ParseFloat(EcmaRuntimeCallInfo * argv)153 JSTaggedValue BuiltinsNumber::ParseFloat(EcmaRuntimeCallInfo *argv)
154 {
155     ASSERT(argv);
156     BUILTINS_API_TRACE(argv->GetThread(), Number, ParseFloat);
157     JSThread *thread = argv->GetThread();
158     JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
159     if (msg->IsUndefined()) {
160         return GetTaggedDouble(base::NAN_VALUE);
161     }
162     [[maybe_unused]] EcmaHandleScope handleScope(thread);
163     // 1. Let inputString be ToString(string).
164     JSHandle<EcmaString> numberString = JSTaggedValue::ToString(thread, msg);
165     // 2. ReturnIfAbrupt(inputString).
166     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
167     CVector<uint8_t> buf;
168     Span<const uint8_t> str = EcmaStringAccessor(numberString).ToUtf8Span(thread, buf);
169     // 4. If neither trimmedString nor any prefix of trimmedString satisfies the syntax of a StrDecimalLiteral
170     // (see 7.1.3.1), return NaN.
171     if (NumberHelper::IsEmptyString(str.begin(), str.end())) {
172         return BuiltinsBase::GetTaggedDouble(base::NAN_VALUE);
173     }
174     double result = NumberHelper::StringToDouble(str.begin(), str.end(), 0, base::IGNORE_TRAILING);
175     return GetTaggedDouble(result);
176 }
177 
178 // 18.2.5
179 // 20.1.2.13
ParseInt(EcmaRuntimeCallInfo * argv)180 JSTaggedValue BuiltinsNumber::ParseInt(EcmaRuntimeCallInfo *argv)
181 {
182     ASSERT(argv);
183     BUILTINS_API_TRACE(argv->GetThread(), Number, ParseInt);
184     JSThread *thread = argv->GetThread();
185     [[maybe_unused]] EcmaHandleScope handleScope(thread);
186     JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
187     JSHandle<JSTaggedValue> arg2 = GetCallArg(argv, 1);
188     int32_t radix = 0;
189 
190     // 1. Let inputString be ToString(string).
191     JSHandle<EcmaString> numberString = JSTaggedValue::ToString(thread, msg);
192     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
193     if (!arg2->IsUndefined()) {
194         // 7. Let R = ToInt32(radix).
195         radix = JSTaggedValue::ToInt32(thread, arg2);
196         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
197     }
198 
199     return NumberHelper::StringToNumber(thread, *numberString, radix);
200 }
201 
202 // prototype
203 // 20.1.3.2
ToExponential(EcmaRuntimeCallInfo * argv)204 JSTaggedValue BuiltinsNumber::ToExponential(EcmaRuntimeCallInfo *argv)
205 {
206     ASSERT(argv);
207     JSThread *thread = argv->GetThread();
208     BUILTINS_API_TRACE(thread, Number, ToExponential);
209     [[maybe_unused]] EcmaHandleScope handleScope(thread);
210     // 1. Let x be ? thisNumberValue(this value).
211     JSTaggedNumber value = ThisNumberValue(thread, argv);
212     // 2. ReturnIfAbrupt(x).
213     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
214 
215     // 3. Let f be ToInteger(fractionDigits).
216     JSHandle<JSTaggedValue> digits = GetCallArg(argv, 0);
217     JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digits);
218     // 5. ReturnIfAbrupt(f).
219     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
220 
221     double values = value.GetNumber();
222     // 6. If x is NaN, return the String "NaN".
223     if (std::isnan(values)) {
224         return GetTaggedString(thread, "NaN");
225     }
226     // 8. If x < 0, then
227     //    a. Let s be "-".
228     //    b. Let x = –x.
229     // 9. If x = +infinity, then
230     //    a. Return the concatenation of the Strings s and "Infinity".
231     if (!std::isfinite(values)) {
232         if (values < 0) {
233             return GetTaggedString(thread, "-Infinity");
234         }
235         return GetTaggedString(thread, "Infinity");
236     }
237 
238     // 4. Assert: f is 0, when fractionDigits is undefined.
239     // 10. If f < 0 or f > 20, throw a RangeError exception
240     double fraction = digitInt.GetNumber();
241     if (digits->IsUndefined()) {
242         fraction = 0;
243     } else {
244         if (fraction < base::MIN_FRACTION || fraction > base::MAX_FRACTION) {
245             THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception());
246         }
247         fraction++;
248     }
249     return NumberHelper::DoubleToExponential(thread, values, static_cast<int>(fraction));
250 }
251 
252 // 20.1.3.3
ToFixed(EcmaRuntimeCallInfo * argv)253 JSTaggedValue BuiltinsNumber::ToFixed(EcmaRuntimeCallInfo *argv)
254 {
255     ASSERT(argv);
256     JSThread *thread = argv->GetThread();
257     BUILTINS_API_TRACE(thread, Number, ToFixed);
258     [[maybe_unused]] EcmaHandleScope handleScope(thread);
259     // 1. Let x be ? thisNumberValue(this value).
260     JSTaggedNumber value = ThisNumberValue(thread, argv);
261     // 2. ReturnIfAbrupt(x).
262     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
263     // 3. Let f be ToInteger(fractionDigits). (If fractionDigits is undefined, this step produces the value 0).
264     JSHandle<JSTaggedValue> digitArgv = GetCallArg(argv, 0);
265     JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv);
266     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
267     if (digitArgv->IsUndefined()) {
268         digitInt = JSTaggedNumber(0);
269     }
270     // 4. ReturnIfAbrupt(f).
271     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
272     double digit = digitInt.GetNumber();
273     if (digit < base::MIN_FRACTION || digit > base::MAX_FRACTION) {
274         THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 0 to 100", JSTaggedValue::Exception());
275     }
276 
277     // 6. If x is NaN, return the String "NaN".
278     double valueNumber = value.GetNumber();
279     if (std::isnan(valueNumber)) {
280         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
281         return globalConst->GetNanCapitalString();
282     }
283     if (!std::isfinite(valueNumber)) {
284         if (valueNumber < 0) {
285             return GetTaggedString(thread, "-Infinity");
286         }
287         return GetTaggedString(thread, "Infinity");
288     }
289     // 9. If x  1021, then
290     //    a. Let m = ToString(x).
291     const double FIRST_NO_FIXED = 1e21;
292     if (std::abs(valueNumber) >= FIRST_NO_FIXED) {
293         return value.ToString(thread).GetTaggedValue();
294     }
295     return NumberHelper::DoubleToFixedString(thread, valueNumber, static_cast<int>(digit));
296 }
297 
298 // 20.1.3.4
ToLocaleString(EcmaRuntimeCallInfo * argv)299 JSTaggedValue BuiltinsNumber::ToLocaleString(EcmaRuntimeCallInfo *argv)
300 {
301     ASSERT(argv);
302     JSThread *thread = argv->GetThread();
303     BUILTINS_API_TRACE(thread, Number, ToLocaleString);
304     [[maybe_unused]] EcmaHandleScope handleScope(thread);
305     // 1. Let x be ? thisNumberValue(this value).
306     [[maybe_unused]] JSHandle<JSTaggedValue> x(thread, ThisNumberValue(thread, argv));
307     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
308 
309     JSHandle<JSTaggedValue> locales = GetCallArg(argv, 0);
310     JSHandle<JSTaggedValue> options = GetCallArg(argv, 1);
311     [[maybe_unused]] bool cacheable = (locales->IsUndefined() || locales->IsString()) && options->IsUndefined();
312 #ifdef ARK_SUPPORT_INTL
313     if (cacheable) {
314         auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales);
315         if (numberFormatter != nullptr) {
316             JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue());
317             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
318             return result.GetTaggedValue();
319         }
320     }
321     // 2. Let numberFormat be ? Construct(%NumberFormat%, « locales, options »).
322     EcmaVM *ecmaVm = thread->GetEcmaVM();
323     JSHandle<JSFunction> ctor(ecmaVm->GetGlobalEnv()->GetNumberFormatFunction());
324     ObjectFactory *factory = ecmaVm->GetFactory();
325     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(ctor);
326     JSHandle<JSNumberFormat> numberFormat = JSHandle<JSNumberFormat>::Cast(obj);
327     JSNumberFormat::InitializeNumberFormat(thread, numberFormat, locales, options, cacheable);
328     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
329     if (cacheable) {
330         auto numberFormatter = JSNumberFormat::GetCachedIcuNumberFormatter(thread, locales);
331         ASSERT(numberFormatter != nullptr);
332         JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormatter, x.GetTaggedValue());
333         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
334         return result.GetTaggedValue();
335     }
336 
337     // Return ? FormatNumeric(numberFormat, x).
338     JSHandle<JSTaggedValue> result = JSNumberFormat::FormatNumeric(thread, numberFormat, x.GetTaggedValue());
339     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
340     return result.GetTaggedValue();
341 #else
342 #ifdef ARK_NOT_SUPPORT_INTL_GLOBAL
343     ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, "LocaleCompare");
344 #else
345     intl::GlobalIntlHelper gh(thread, intl::GlobalFormatterType::NumberFormatter);
346     auto numberFormatter = gh.GetGlobalObject<intl::GlobalNumberFormat>(thread,
347         locales, options, intl::GlobalFormatterType::NumberFormatter, cacheable);
348     if (numberFormatter == nullptr) {
349         LOG_ECMA(ERROR) << "BuiltinsNumber:numberFormatter is nullptr";
350     }
351     ASSERT(numberFormatter != nullptr);
352     std::string result = numberFormatter->Format(x->GetDouble());
353     EcmaVM *ecmaVm = thread->GetEcmaVM();
354     ObjectFactory *factory = ecmaVm->GetFactory();
355     JSHandle returnValue = factory->NewFromStdString(result);
356     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
357     return returnValue.GetTaggedValue();
358 #endif
359 #endif
360 }
361 
362 // 20.1.3.5
ToPrecision(EcmaRuntimeCallInfo * argv)363 JSTaggedValue BuiltinsNumber::ToPrecision(EcmaRuntimeCallInfo *argv)
364 {
365     ASSERT(argv);
366     JSThread *thread = argv->GetThread();
367     BUILTINS_API_TRACE(thread, Number, ToPrecision);
368     [[maybe_unused]] EcmaHandleScope handleScope(thread);
369     // 1. Let x be ? thisNumberValue(this value).
370     JSTaggedNumber value = ThisNumberValue(thread, argv);
371     // 2. ReturnIfAbrupt(x).
372     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
373 
374     // 3. If precision is undefined, return ToString(x).
375     JSHandle<JSTaggedValue> digitArgv = GetCallArg(argv, 0);
376     if (digitArgv->IsUndefined()) {
377         return value.ToString(thread).GetTaggedValue();
378     }
379     // 4. Let p be ToInteger(precision).
380     JSTaggedNumber digitInt = JSTaggedValue::ToInteger(thread, digitArgv);
381     // 5. ReturnIfAbrupt(p).
382     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
383 
384     // 6. If x is NaN, return the String "NaN".
385     double valueNumber = value.GetNumber();
386     if (std::isnan(valueNumber)) {
387         return GetTaggedString(thread, "NaN");
388     }
389     // 9. If x = +infinity, then
390     //    a. Return the String that is the concatenation of s and "Infinity".
391     if (!std::isfinite(valueNumber)) {
392         if (valueNumber < 0) {
393             return GetTaggedString(thread, "-Infinity");
394         }
395         return GetTaggedString(thread, "Infinity");
396     }
397 
398     // If p < 1 or p > 21, throw a RangeError exception
399     double digit = digitInt.GetNumber();
400     if (digit < base::MIN_FRACTION + 1 || digit > base::MAX_FRACTION) {
401         THROW_RANGE_ERROR_AND_RETURN(thread, "fraction must be 1 to 100", JSTaggedValue::Exception());
402     }
403     return NumberHelper::DoubleToPrecisionString(thread, valueNumber, static_cast<int>(digit));
404 }
405 
406 // 20.1.3.6
ToString(EcmaRuntimeCallInfo * argv)407 JSTaggedValue BuiltinsNumber::ToString(EcmaRuntimeCallInfo *argv)
408 {
409     ASSERT(argv);
410     JSThread *thread = argv->GetThread();
411     BUILTINS_API_TRACE(thread, Number, ToString);
412     // NOTE: There is no heap alloc in fast path, so delay the scope.
413 
414     // 1. Let x be ? thisNumberValue(this value).
415     JSTaggedNumber value = ThisNumberValue(thread, argv);
416     // 2. ReturnIfAbrupt(x).
417     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
418 
419     // 3. If radix is not present, let radixNumber be 10.
420     // 4. Else if radix is undefined, let radixNumber be 10.
421     double radix = base::DECIMAL;
422     JSHandle<JSTaggedValue> radixValue = GetCallArg(argv, 0);
423     // 5. Else let radixNumber be ToInteger(radix).
424     if (radixValue->IsInt()) {
425         radix = radixValue->GetInt();
426     } else if (!radixValue->IsUndefined()) {
427         JSTaggedNumber radixNumber = JSTaggedValue::ToInteger(thread, radixValue);
428         // 6. ReturnIfAbrupt(x).
429         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
430         radix = radixNumber.GetNumber();
431     }
432 
433     // 7. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
434     if (radix < base::MIN_RADIX || radix > base::MAX_RADIX) {
435         THROW_RANGE_ERROR_AND_RETURN(thread, "radix must be 2 to 36", JSTaggedValue::Exception());
436     }
437     // 8. If radixNumber = 10, return ToString(x).
438     if (radix == base::DECIMAL) {
439         JSHandle<NumberToStringResultCache> cacheTable(thread->GetGlobalEnv()->GetNumberToStringResultCache());
440         int entry = cacheTable->GetNumberHash(value);
441         JSTaggedValue cacheResult =  cacheTable->FindCachedResult(thread, entry, value);
442         if (cacheResult != JSTaggedValue::Undefined()) {
443             return cacheResult;
444         }
445         // NOTE: There is no heap alloc before here, so delay the scope to here.
446         [[maybe_unused]] EcmaHandleScope handleScope(thread);
447         JSHandle<EcmaString> resultJSHandle = value.ToString(thread);
448         cacheTable->SetCachedResult(thread, entry, value, resultJSHandle);
449         return resultJSHandle.GetTaggedValue();
450     }
451     // NOTE: There is no heap alloc before here, so delay the scope to here.
452     [[maybe_unused]] EcmaHandleScope handleScope(thread);
453     if (value.IsInt()) {
454         return NumberHelper::Int32ToString(thread, value.GetInt(), radix);
455     }
456 
457     double valueNumber = value.GetNumber();
458     // If x is NaN, return the String "NaN".
459     if (std::isnan(valueNumber)) {
460         return GetTaggedString(thread, "NaN");
461     }
462     //  If x = +infinity, then
463     //     Return the String that is the concatenation of s and "Infinity".
464     if (!std::isfinite(valueNumber)) {
465         if (valueNumber < 0) {
466             return GetTaggedString(thread, "-Infinity");
467         }
468         return GetTaggedString(thread, "Infinity");
469     }
470     return NumberHelper::DoubleToString(thread, valueNumber, static_cast<int>(radix));
471 }
472 
473 // 20.1.3.7
ValueOf(EcmaRuntimeCallInfo * argv)474 JSTaggedValue BuiltinsNumber::ValueOf(EcmaRuntimeCallInfo *argv)
475 {
476     ASSERT(argv);
477     JSThread *thread = argv->GetThread();
478     BUILTINS_API_TRACE(thread, Number, ValueOf);
479     // 1. Let x be ? thisNumberValue(this value).
480     JSTaggedValue x = ThisNumberValue(thread, argv);
481 
482     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
483     return x;
484 }
485 
ThisNumberValue(JSThread * thread,EcmaRuntimeCallInfo * argv)486 JSTaggedNumber BuiltinsNumber::ThisNumberValue(JSThread *thread, EcmaRuntimeCallInfo *argv)
487 {
488     BUILTINS_API_TRACE(thread, Number, ThisNumberValue);
489     JSHandle<JSTaggedValue> value = GetThis(argv);
490     if (value->IsNumber()) {
491         return JSTaggedNumber(value.GetTaggedValue());
492     }
493     if (value->IsJSPrimitiveRef()) {
494         JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(thread);
495         if (primitive.IsNumber()) {
496             return JSTaggedNumber(primitive);
497         }
498     }
499     [[maybe_unused]] EcmaHandleScope handleScope(thread);
500     THROW_TYPE_ERROR_AND_RETURN(thread, "not number type", JSTaggedNumber::Exception());
501 }
502 
CreateCacheTable(const JSThread * thread)503 JSTaggedValue NumberToStringResultCache::CreateCacheTable(const JSThread *thread)
504 {
505     int length = INITIAL_CACHE_NUMBER * ENTRY_SIZE;
506     auto table = static_cast<NumberToStringResultCache*>(
507         *thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()));
508     return JSTaggedValue(table);
509 }
510 
FindCachedResult(const JSThread * thread,int entry,JSTaggedValue & target)511 JSTaggedValue NumberToStringResultCache::FindCachedResult(const JSThread *thread, int entry, JSTaggedValue &target)
512 {
513     uint32_t index = static_cast<uint32_t>(entry * ENTRY_SIZE);
514     JSTaggedValue entryNumber = Get(thread, index + NUMBER_INDEX);
515     if (entryNumber == target) {
516         return Get(thread, index + RESULT_INDEX);
517     }
518     return JSTaggedValue::Undefined();
519 }
520 
SetCachedResult(const JSThread * thread,int entry,JSTaggedValue & number,JSHandle<EcmaString> & result)521 void NumberToStringResultCache::SetCachedResult(const JSThread *thread, int entry, JSTaggedValue &number,
522     JSHandle<EcmaString> &result)
523 {
524     uint32_t index = static_cast<uint32_t>(entry * ENTRY_SIZE);
525     Set(thread, index + NUMBER_INDEX, number);
526     Set(thread, index + RESULT_INDEX, result.GetTaggedValue());
527 }
528 }  // namespace panda::ecmascript::builtins
529