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