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