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