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
28namespace panda::ecmascript::builtins {
29using NumberHelper = base::NumberHelper;
30
31JSTaggedValue 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
76JSTaggedValue 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
90JSTaggedValue 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
113JSTaggedValue 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
128JSTaggedValue 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
152JSTaggedValue 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
179JSTaggedValue 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
203JSTaggedValue 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
252JSTaggedValue 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
298JSTaggedValue 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
362JSTaggedValue 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
406JSTaggedValue 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
473JSTaggedValue 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
485JSTaggedNumber 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
502JSTaggedValue 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
510JSTaggedValue 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
520void 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