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