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_math.h"
17 #include <random>
18 #include <sys/time.h>
19
20 #include "ecmascript/ecma_runtime_call_info.h"
21 #include "ecmascript/js_tagged_number.h"
22 #include "ecmascript/js_tagged_value-inl.h"
23
24 namespace panda::ecmascript::builtins {
25 using NumberHelper = base::NumberHelper;
26 using RandomGenerator = base::RandomGenerator;
27
28 // 20.2.2.1
Abs(EcmaRuntimeCallInfo * argv)29 JSTaggedValue BuiltinsMath::Abs(EcmaRuntimeCallInfo *argv)
30 {
31 ASSERT(argv);
32 BUILTINS_API_TRACE(argv->GetThread(), Math, Abs);
33 JSThread *thread = argv->GetThread();
34 [[maybe_unused]] EcmaHandleScope handleScope(thread);
35 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
36 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
37 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
38 if (numberValue.IsDouble()) {
39 // if number_value is double,NaN,Undefine, deal in this case
40 // if number_value is a String ,which can change to double. e.g."100",deal in this case
41 return GetTaggedDouble(std::fabs(numberValue.GetDouble()));
42 }
43 // if number_value is int,boolean,null, deal in this case
44 return GetTaggedInt(std::abs(numberValue.GetInt()));
45 }
46
47 // 20.2.2.2
Acos(EcmaRuntimeCallInfo * argv)48 JSTaggedValue BuiltinsMath::Acos(EcmaRuntimeCallInfo *argv)
49 {
50 ASSERT(argv);
51 BUILTINS_API_TRACE(argv->GetThread(), Math, Acos);
52 JSThread *thread = argv->GetThread();
53 [[maybe_unused]] EcmaHandleScope handleScope(thread);
54 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
55 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
56 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
57 double value = numberValue.GetNumber();
58 double result = base::NAN_VALUE;
59 // value == -NaN , <-1 or > 1,result is NaN
60 if (!std::isnan(std::abs(value)) && value <= 1 && value >= -1) {
61 result = std::acos(value);
62 }
63 return GetTaggedDouble(result);
64 }
65
66 // 20.2.2.3
Acosh(EcmaRuntimeCallInfo * argv)67 JSTaggedValue BuiltinsMath::Acosh(EcmaRuntimeCallInfo *argv)
68 {
69 ASSERT(argv);
70 BUILTINS_API_TRACE(argv->GetThread(), Math, Acosh);
71 JSThread *thread = argv->GetThread();
72 [[maybe_unused]] EcmaHandleScope handleScope(thread);
73 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
74 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
75 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
76 double value = numberValue.GetNumber();
77 double result = base::NAN_VALUE;
78 if (value >= 1) {
79 result = std::acosh(value);
80 }
81 return GetTaggedDouble(result);
82 }
83
84 // 20.2.2.4
Asin(EcmaRuntimeCallInfo * argv)85 JSTaggedValue BuiltinsMath::Asin(EcmaRuntimeCallInfo *argv)
86 {
87 ASSERT(argv);
88 BUILTINS_API_TRACE(argv->GetThread(), Math, Asin);
89 JSThread *thread = argv->GetThread();
90 [[maybe_unused]] EcmaHandleScope handleScope(thread);
91 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
92 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
93 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
94 double value = numberValue.GetNumber();
95 double result = base::NAN_VALUE;
96 if (value >= -1 && value <= 1) {
97 result = std::asin(value);
98 }
99 return GetTaggedDouble(result);
100 }
101
102 // 20.2.2.5
Asinh(EcmaRuntimeCallInfo * argv)103 JSTaggedValue BuiltinsMath::Asinh(EcmaRuntimeCallInfo *argv)
104 {
105 ASSERT(argv);
106 BUILTINS_API_TRACE(argv->GetThread(), Math, Asinh);
107 JSThread *thread = argv->GetThread();
108 [[maybe_unused]] EcmaHandleScope handleScope(thread);
109 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
110 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
111 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
112 double value = numberValue.GetNumber();
113 double result = base::NAN_VALUE;
114 // value == -NaN, NaN, result is NaN
115 if (!std::isnan(std::abs(value))) {
116 result = base::MathHelper::Asinh(value);
117 }
118 return GetTaggedDouble(result);
119 }
120
121 // 20.2.2.6
Atan(EcmaRuntimeCallInfo * argv)122 JSTaggedValue BuiltinsMath::Atan(EcmaRuntimeCallInfo *argv)
123 {
124 ASSERT(argv);
125 BUILTINS_API_TRACE(argv->GetThread(), Math, Atan);
126 JSThread *thread = argv->GetThread();
127 [[maybe_unused]] EcmaHandleScope handleScope(thread);
128 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
129 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
130 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
131 double value = numberValue.GetNumber();
132 double result = base::NAN_VALUE;
133 // value == -NaN, NaN, result is NaN
134 if (!std::isnan(std::abs(value))) {
135 result = std::atan(value);
136 }
137 return GetTaggedDouble(result);
138 }
139
140 // 20.2.2.7
Atanh(EcmaRuntimeCallInfo * argv)141 JSTaggedValue BuiltinsMath::Atanh(EcmaRuntimeCallInfo *argv)
142 {
143 ASSERT(argv);
144 BUILTINS_API_TRACE(argv->GetThread(), Math, Atanh);
145 JSThread *thread = argv->GetThread();
146 [[maybe_unused]] EcmaHandleScope handleScope(thread);
147 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
148 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
149 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
150 double value = numberValue.GetNumber();
151 double result = base::NAN_VALUE;
152 if (value >= -1 && value <= 1) {
153 result = base::MathHelper::Atanh(value);
154 }
155 return GetTaggedDouble(result);
156 }
157
158 // 20.2.2.8
Atan2(EcmaRuntimeCallInfo * argv)159 JSTaggedValue BuiltinsMath::Atan2(EcmaRuntimeCallInfo *argv)
160 {
161 ASSERT(argv);
162 BUILTINS_API_TRACE(argv->GetThread(), Math, Atan2);
163 JSThread *thread = argv->GetThread();
164 [[maybe_unused]] EcmaHandleScope handleScope(thread);
165 JSHandle<JSTaggedValue> msgY = GetCallArg(argv, 0);
166 JSHandle<JSTaggedValue> msgX = GetCallArg(argv, 1);
167 double result = base::NAN_VALUE;
168 JSTaggedNumber numberValueY = JSTaggedValue::ToNumber(thread, msgY);
169 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
170 JSTaggedNumber numberValueX = JSTaggedValue::ToNumber(thread, msgX);
171 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
172 double valueY = numberValueY.GetNumber();
173 double valueX = numberValueX.GetNumber();
174 // y = +0 and x > +0, return +0
175 // y = -0 and x > +0, return -0
176 if (valueY == 0 && valueX > 0) {
177 result = valueY;
178 } else if (std::isfinite(valueY) && valueX == std::numeric_limits<double>::infinity()) {
179 // y < 0 and y is finite and x is POSITIVE_INFINITY,return -0
180 // y >= 0 and y is finite and x is POSITIVE_INFINITY,return +0
181 result = valueY >= 0 ? 0 : -0.0;
182 } else if (!std::isnan(std::abs(valueY)) && !std::isnan(std::abs(valueX))) {
183 // If either x or y is NaN, the result is NaN
184 result = std::atan2(valueY, valueX);
185 }
186 return GetTaggedDouble(result);
187 }
188
189 // 20.2.2.9
Cbrt(EcmaRuntimeCallInfo * argv)190 JSTaggedValue BuiltinsMath::Cbrt(EcmaRuntimeCallInfo *argv)
191 {
192 ASSERT(argv);
193 BUILTINS_API_TRACE(argv->GetThread(), Math, Cbrt);
194 JSThread *thread = argv->GetThread();
195 [[maybe_unused]] EcmaHandleScope handleScope(thread);
196 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
197 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
198 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
199 double value = numberValue.GetNumber();
200 double result = base::NAN_VALUE;
201 // if value == -NaN, NaN, result is NaN
202 if (!std::isnan(std::abs(value))) {
203 result = std::cbrt(value);
204 }
205 return GetTaggedDouble(result);
206 }
207
208 // 20.2.2.10
Ceil(EcmaRuntimeCallInfo * argv)209 JSTaggedValue BuiltinsMath::Ceil(EcmaRuntimeCallInfo *argv)
210 {
211 ASSERT(argv);
212 BUILTINS_API_TRACE(argv->GetThread(), Math, Ceil);
213 JSThread *thread = argv->GetThread();
214 [[maybe_unused]] EcmaHandleScope handleScope(thread);
215 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
216 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
217 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
218 double value = numberValue.GetNumber();
219 double result = base::NAN_VALUE;
220 // If value is NaN or -NaN, +infinite, -infinite,return value
221 if (!std::isfinite(value)) {
222 // if value is -NaN , return NaN, else return value
223 if (!std::isnan(std::abs(value))) {
224 result = value;
225 }
226 } else {
227 result = std::ceil(value);
228 }
229 return GetTaggedDouble(result);
230 }
231
232 // 20.2.2.11
Clz32(EcmaRuntimeCallInfo * argv)233 JSTaggedValue BuiltinsMath::Clz32(EcmaRuntimeCallInfo *argv)
234 {
235 ASSERT(argv);
236 BUILTINS_API_TRACE(argv->GetThread(), Math, Clz32);
237 JSThread *thread = argv->GetThread();
238 [[maybe_unused]] EcmaHandleScope handleScope(thread);
239 constexpr int defaultValue = 32;
240 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
241 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
242 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
243 double value = numberValue.GetNumber();
244 auto tmpValue = std::abs(value);
245 auto result = numberValue.ToUint32();
246 if (!std::isfinite(tmpValue) || tmpValue == 0 || result == 0) {
247 // If value is NaN or -NaN, +infinite, -infinite, 0,return 32
248 return GetTaggedInt(defaultValue);
249 }
250 return GetTaggedInt(__builtin_clz(result));
251 }
252
253 // 20.2.2.12
Cos(EcmaRuntimeCallInfo * argv)254 JSTaggedValue BuiltinsMath::Cos(EcmaRuntimeCallInfo *argv)
255 {
256 ASSERT(argv);
257 BUILTINS_API_TRACE(argv->GetThread(), Math, Cos);
258 JSThread *thread = argv->GetThread();
259 [[maybe_unused]] EcmaHandleScope handleScope(thread);
260 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
261 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
262 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
263 double value = numberValue.GetNumber();
264 double result = base::NAN_VALUE;
265 // If value is NaN or -NaN, +infinite, -infinite, result is NaN
266 if (std::isfinite(std::abs(value))) {
267 result = std::cos(value);
268 }
269 return GetTaggedDouble(result);
270 }
271
272 // 20.2.2.13
Cosh(EcmaRuntimeCallInfo * argv)273 JSTaggedValue BuiltinsMath::Cosh(EcmaRuntimeCallInfo *argv)
274 {
275 ASSERT(argv);
276 BUILTINS_API_TRACE(argv->GetThread(), Math, Cosh);
277 JSThread *thread = argv->GetThread();
278 [[maybe_unused]] EcmaHandleScope handleScope(thread);
279 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
280 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
281 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
282 double value = numberValue.GetNumber();
283 double result = base::NAN_VALUE;
284 // if value is NaN or -NaN, result is NaN
285 if (!std::isnan(std::abs(value))) {
286 result = std::cosh(value);
287 }
288 return GetTaggedDouble(result);
289 }
290
291 // 20.2.2.14
Exp(EcmaRuntimeCallInfo * argv)292 JSTaggedValue BuiltinsMath::Exp(EcmaRuntimeCallInfo *argv)
293 {
294 ASSERT(argv);
295 BUILTINS_API_TRACE(argv->GetThread(), Math, Exp);
296 JSThread *thread = argv->GetThread();
297 [[maybe_unused]] EcmaHandleScope handleScope(thread);
298 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
299 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
300 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
301 double value = numberValue.GetNumber();
302 double result = base::NAN_VALUE;
303 // if value is NaN or -NaN, result is NaN
304 if (!std::isnan(std::abs(value))) {
305 result = std::exp(value);
306 }
307 return GetTaggedDouble(result);
308 }
309
310 // 20.2.2.15
Expm1(EcmaRuntimeCallInfo * argv)311 JSTaggedValue BuiltinsMath::Expm1(EcmaRuntimeCallInfo *argv)
312 {
313 ASSERT(argv);
314 BUILTINS_API_TRACE(argv->GetThread(), Math, Expm1);
315 JSThread *thread = argv->GetThread();
316 [[maybe_unused]] EcmaHandleScope handleScope(thread);
317 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
318 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
319 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
320 double value = numberValue.GetNumber();
321 double result = base::NAN_VALUE;
322 // if value is NaN or -NaN, result is NaN
323 if (!std::isnan(std::abs(value))) {
324 result = std::expm1(value);
325 }
326 return GetTaggedDouble(result);
327 }
328
329 // 20.2.2.16
Floor(EcmaRuntimeCallInfo * argv)330 JSTaggedValue BuiltinsMath::Floor(EcmaRuntimeCallInfo *argv)
331 {
332 ASSERT(argv);
333 BUILTINS_API_TRACE(argv->GetThread(), Math, Floor);
334 JSThread *thread = argv->GetThread();
335 [[maybe_unused]] EcmaHandleScope handleScope(thread);
336 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
337 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
338 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
339 double value = numberValue.GetNumber();
340 double result = base::NAN_VALUE;
341 // If value is NaN or -NaN, +infinite, -infinite, +0, -0, return value
342 if (!std::isfinite(value) || value == 0) {
343 // If value is -NaN, return NaN, else return value
344 if (!std::isnan(std::abs(value))) {
345 result = value;
346 }
347 } else if (value > 0 && value < 1) {
348 // If x is greater than 0 but less than 1, the result is +0
349 result = 0;
350 } else {
351 result = std::floor(value);
352 }
353 return GetTaggedDouble(result);
354 }
355
356 // 20.2.2.17
Fround(EcmaRuntimeCallInfo * argv)357 JSTaggedValue BuiltinsMath::Fround(EcmaRuntimeCallInfo *argv)
358 {
359 ASSERT(argv);
360 BUILTINS_API_TRACE(argv->GetThread(), Math, Fround);
361 JSThread *thread = argv->GetThread();
362 [[maybe_unused]] EcmaHandleScope handleScope(thread);
363 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
364 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
365 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
366 double value = numberValue.GetNumber();
367 double result;
368 if (std::isnan(std::abs(value))) {
369 // If result is NaN or -NaN, the result is NaN
370 result = base::NAN_VALUE;
371 } else {
372 result = static_cast<float>(value);
373 }
374 return GetTaggedDouble(result);
375 }
376
377 // 20.2.2.18
Hypot(EcmaRuntimeCallInfo * argv)378 JSTaggedValue BuiltinsMath::Hypot(EcmaRuntimeCallInfo *argv)
379 {
380 ASSERT(argv);
381 BUILTINS_API_TRACE(argv->GetThread(), Math, Hypot);
382 JSThread *thread = argv->GetThread();
383 [[maybe_unused]] EcmaHandleScope handleScope(thread);
384 double result = 0;
385 double value = 0;
386 uint32_t argLen = argv->GetArgsNumber();
387 auto numberValue = JSTaggedNumber(0);
388 for (uint32_t i = 0; i < argLen; i++) {
389 JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
390 numberValue = JSTaggedValue::ToNumber(thread, msg);
391 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
392 value = numberValue.GetNumber();
393 result = std::hypot(result, value);
394 }
395 return GetTaggedDouble(result);
396 }
397
398 // 20.2.2.19
Imul(EcmaRuntimeCallInfo * argv)399 JSTaggedValue BuiltinsMath::Imul(EcmaRuntimeCallInfo *argv)
400 {
401 ASSERT(argv);
402 BUILTINS_API_TRACE(argv->GetThread(), Math, Imul);
403 JSThread *thread = argv->GetThread();
404 [[maybe_unused]] EcmaHandleScope handleScope(thread);
405 JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 0);
406 JSHandle<JSTaggedValue> msg2 = GetCallArg(argv, 1);
407 JSTaggedNumber numberValue1 = JSTaggedValue::ToNumber(thread, msg1);
408 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
409 JSTaggedNumber numberValue2 = JSTaggedValue::ToNumber(thread, msg2);
410 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
411 auto value1 = numberValue1.GetNumber();
412 auto value2 = numberValue2.GetNumber();
413 if (!std::isfinite(value1) || !std::isfinite(value2)) {
414 // If value is NaN or -NaN, +infinite, -infinite
415 return GetTaggedInt(0);
416 }
417 value1 = numberValue1.ToInt32();
418 value2 = numberValue2.ToInt32();
419 // purposely ignoring overflow
420 auto result = static_cast<int32_t>(static_cast<int64_t>(value1) * static_cast<int64_t>(value2));
421 return GetTaggedInt(result);
422 }
423
424 // 20.2.2.20
Log(EcmaRuntimeCallInfo * argv)425 JSTaggedValue BuiltinsMath::Log(EcmaRuntimeCallInfo *argv)
426 {
427 ASSERT(argv);
428 BUILTINS_API_TRACE(argv->GetThread(), Math, Log);
429 JSThread *thread = argv->GetThread();
430 [[maybe_unused]] EcmaHandleScope handleScope(thread);
431 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
432 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
433 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
434 double value = numberValue.GetNumber();
435 double result = base::NAN_VALUE;
436 // If value is NaN , -NaN , or < 0,result is NaN
437 if (!std::isnan(std::abs(value)) && value >= 0) {
438 result = std::log(value);
439 }
440 return GetTaggedDouble(result);
441 }
442
443 // 20.2.2.21
Log1p(EcmaRuntimeCallInfo * argv)444 JSTaggedValue BuiltinsMath::Log1p(EcmaRuntimeCallInfo *argv)
445 {
446 ASSERT(argv);
447 BUILTINS_API_TRACE(argv->GetThread(), Math, Log1p);
448 JSThread *thread = argv->GetThread();
449 [[maybe_unused]] EcmaHandleScope handleScope(thread);
450 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
451 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
452 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
453 double value = numberValue.GetNumber();
454 double result = base::NAN_VALUE;
455 // If value is NaN , -NaN , or < -1,result is NaN
456 if (!std::isnan(std::abs(value)) && value >= -1) {
457 result = std::log1p(value);
458 }
459 return GetTaggedDouble(result);
460 }
461
462 // 20.2.2.22
Log10(EcmaRuntimeCallInfo * argv)463 JSTaggedValue BuiltinsMath::Log10(EcmaRuntimeCallInfo *argv)
464 {
465 ASSERT(argv);
466 BUILTINS_API_TRACE(argv->GetThread(), Math, Log10);
467 JSThread *thread = argv->GetThread();
468 [[maybe_unused]] EcmaHandleScope handleScope(thread);
469 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
470 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
471 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
472 double value = numberValue.GetNumber();
473 double result = base::NAN_VALUE;
474 // If value is NaN , -NaN , or < 0,result is NaN
475 if (!std::isnan(std::abs(value)) && value >= 0) {
476 result = std::log10(value);
477 }
478 return GetTaggedDouble(result);
479 }
480
481 // 20.2.2.23
Log2(EcmaRuntimeCallInfo * argv)482 JSTaggedValue BuiltinsMath::Log2(EcmaRuntimeCallInfo *argv)
483 {
484 ASSERT(argv);
485 BUILTINS_API_TRACE(argv->GetThread(), Math, Log2);
486 JSThread *thread = argv->GetThread();
487 [[maybe_unused]] EcmaHandleScope handleScope(thread);
488 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
489 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
490 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
491 double value = numberValue.GetNumber();
492 double result = base::NAN_VALUE;
493 // If value is NaN , -NaN , or < 0,result is NaN
494 if (!std::isnan(std::abs(value)) && value >= 0) {
495 result = std::log2(value);
496 }
497 return GetTaggedDouble(result);
498 }
499
IsNegZero(double value)500 inline bool IsNegZero(double value)
501 {
502 return (value == 0.0 && (base::bit_cast<uint64_t>(value) & base::DOUBLE_SIGN_MASK) == base::DOUBLE_SIGN_MASK);
503 }
504
505 // 20.2.2.24
Max(EcmaRuntimeCallInfo * argv)506 JSTaggedValue BuiltinsMath::Max(EcmaRuntimeCallInfo *argv)
507 {
508 ASSERT(argv);
509 BUILTINS_API_TRACE(argv->GetThread(), Math, Max);
510 JSThread *thread = argv->GetThread();
511 [[maybe_unused]] EcmaHandleScope handleScope(thread);
512 uint32_t argLen = argv->GetArgsNumber();
513 // 1. Let coerced be a new empty List.
514 // 2. For each element arg of args, do
515 // a. Let n be ? ToNumber(arg).
516 // b. Append n to coerced.
517 std::vector<JSTaggedNumber> numberList(argLen, JSTaggedNumber(-base::POSITIVE_INFINITY));
518 for (uint32_t i = 0; i < argLen; i++) {
519 JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
520 numberList[i] = JSTaggedValue::ToNumber(thread, msg);
521 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
522 }
523 // 3. Let highest be -∞.
524 // 4. For each element number of coerced, do
525 // a. If number is NaN, return NaN.
526 // b. If number is +0 and highest is -0, set highest to +0.
527 // c. If number > highest, set highest to number.
528 auto result = JSTaggedNumber(-base::POSITIVE_INFINITY);
529 auto tmpMax = -base::POSITIVE_INFINITY;
530 auto value = -base::POSITIVE_INFINITY;
531 for (uint32_t i = 0; i < argLen; i++) {
532 value = numberList[i].GetNumber();
533 if (std::isnan(std::abs(value))) {
534 // If any value is NaN, or -NaN, the max result is NaN
535 result = numberList[i];
536 break;
537 }
538 if (value > tmpMax) {
539 result = numberList[i];
540 tmpMax = value;
541 } else if (value == 0 && tmpMax == 0 && IsNegZero(tmpMax) && !IsNegZero(value)) {
542 // if tmp_max is -0, value is 0, max is 0
543 result = numberList[i];
544 tmpMax = value;
545 }
546 }
547 return result;
548 }
549
550 // 20.2.2.25
Min(EcmaRuntimeCallInfo * argv)551 JSTaggedValue BuiltinsMath::Min(EcmaRuntimeCallInfo *argv)
552 {
553 ASSERT(argv);
554 BUILTINS_API_TRACE(argv->GetThread(), Math, Min);
555 JSThread *thread = argv->GetThread();
556 [[maybe_unused]] EcmaHandleScope handleScope(thread);
557 uint32_t argLen = argv->GetArgsNumber();
558 // 1. Let coerced be a new empty List.
559 // 2. For each element arg of args, do
560 // a. Let n be ? ToNumber(arg).
561 // b. Append n to coerced.
562 std::vector<JSTaggedNumber> numberList(argLen, JSTaggedNumber(base::POSITIVE_INFINITY));
563 for (uint32_t i = 0; i < argLen; i++) {
564 JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
565 numberList[i] = JSTaggedValue::ToNumber(thread, msg);
566 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
567 }
568 // 3. Let lowest be +∞.
569 // 4. For each element number of coerced, do
570 // a. If number is NaN, return NaN.
571 // b. If number is -0 and lowest is +0, set lowest to -0.
572 // c. If number < lowest, set lowest to number.
573 auto result = JSTaggedNumber(base::POSITIVE_INFINITY);
574 auto tmpMin = base::POSITIVE_INFINITY;
575 auto value = base::POSITIVE_INFINITY;
576 for (uint32_t i = 0; i < argLen; i++) {
577 value = numberList[i].GetNumber();
578 if (std::isnan(std::abs(value))) {
579 // If any value is NaN or -NaN, the min result is NaN
580 result = numberList[i];
581 break;
582 }
583 if (value < tmpMin) {
584 result = numberList[i];
585 tmpMin = value;
586 } else if (value == 0 && tmpMin == 0 && !IsNegZero(tmpMin) && IsNegZero(value)) {
587 // if tmp_min is 0, value is -0, min is -0
588 result = numberList[i];
589 tmpMin = value;
590 }
591 }
592 return result;
593 }
594
595 // 20.2.2.26
Pow(EcmaRuntimeCallInfo * argv)596 JSTaggedValue BuiltinsMath::Pow(EcmaRuntimeCallInfo *argv)
597 {
598 ASSERT(argv);
599 BUILTINS_API_TRACE(argv->GetThread(), Math, Pow);
600 JSThread *thread = argv->GetThread();
601 [[maybe_unused]] EcmaHandleScope handleScope(thread);
602 JSHandle<JSTaggedValue> msgX = GetCallArg(argv, 0);
603 JSHandle<JSTaggedValue> msgY = GetCallArg(argv, 1);
604 JSHandle<JSTaggedValue> baseVale = JSTaggedValue::ToNumeric(thread, msgX);
605 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
606 JSHandle<JSTaggedValue> exponentValue = JSTaggedValue::ToNumeric(thread, msgY);
607 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
608 if (baseVale->IsBigInt() || exponentValue->IsBigInt()) {
609 if (baseVale->IsBigInt() && exponentValue->IsBigInt()) {
610 JSHandle<BigInt> bigBaseVale(baseVale);
611 JSHandle<BigInt> bigExponentValue(exponentValue);
612 return BigInt::Exponentiate(thread, bigBaseVale, bigExponentValue).GetTaggedValue();
613 }
614 THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot mix BigInt and other types, use explicit conversions",
615 JSTaggedValue::Exception());
616 }
617 double valueX = baseVale->GetNumber();
618 double valueY = exponentValue->GetNumber();
619 // If abs(x) is 1 and y is inf or -inf, the result is NaN
620 if (std::abs(valueX) == 1 && !std::isfinite(valueY)) {
621 return GetTaggedDouble(base::NAN_VALUE);
622 }
623 double result = std::pow(valueX, valueY);
624 if (std::isnan(std::abs(result))) {
625 // If result is NaN or -NaN, the result is NaN
626 result = base::NAN_VALUE;
627 }
628 return GetTaggedDouble(result);
629 }
630
631 // 20.2.2.27
Random(EcmaRuntimeCallInfo * argv)632 JSTaggedValue BuiltinsMath::Random(EcmaRuntimeCallInfo *argv)
633 {
634 ASSERT(argv);
635 JSThread *thread = argv->GetThread();
636 BUILTINS_API_TRACE(thread, Math, Random);
637 [[maybe_unused]] EcmaHandleScope handleScope(thread);
638 return GetTaggedDouble(RandomGenerator::NextDouble());
639 }
640
641 // 20.2.2.28
Round(EcmaRuntimeCallInfo * argv)642 JSTaggedValue BuiltinsMath::Round(EcmaRuntimeCallInfo *argv)
643 {
644 ASSERT(argv);
645 BUILTINS_API_TRACE(argv->GetThread(), Math, Round);
646 JSThread *thread = argv->GetThread();
647 [[maybe_unused]] EcmaHandleScope handleScope(thread);
648 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
649 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
650 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
651 double value = numberValue.GetNumber();
652 auto result = base::NAN_VALUE;
653 const double diff = 0.5;
654 double absValue = std::abs(value);
655 if (!std::isfinite(absValue) || absValue == 0) {
656 // If value is NaN, +infinite, or -infinite, VRegisterTag is DOUBLE
657 if (!std::isnan(absValue)) {
658 // If value is NaN or -NaN, the result is default NaN, else is value
659 result = value;
660 }
661 return GetTaggedDouble(result);
662 }
663 // If x is less than 0 but greater than or equal to -0.5, the result is -0
664 if (value < 0 && value >= -diff) {
665 return GetTaggedDouble(-0.0);
666 }
667 // If x is greater than 0 but less than 0.5, the result is +0
668 if (value > 0 && value < diff) {
669 return GetTaggedInt(0);
670 }
671 // For huge integers
672 result = std::ceil(value);
673 if (result - value > diff) {
674 result -= 1;
675 }
676 return GetTaggedDouble(result);
677 }
678
679 // 20.2.2.29
Sign(EcmaRuntimeCallInfo * argv)680 JSTaggedValue BuiltinsMath::Sign(EcmaRuntimeCallInfo *argv)
681 {
682 ASSERT(argv);
683 BUILTINS_API_TRACE(argv->GetThread(), Math, Sign);
684 JSThread *thread = argv->GetThread();
685 [[maybe_unused]] EcmaHandleScope handleScope(thread);
686 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
687 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
688 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
689 double value = numberValue.GetNumber();
690 if (std::isnan(std::abs(value))) {
691 return GetTaggedDouble(std::abs(value));
692 }
693 if (value == 0.0) {
694 return GetTaggedDouble(value);
695 }
696 if (value < 0) {
697 return GetTaggedInt(-1);
698 }
699 return GetTaggedInt(1);
700 }
701
702 // 20.2.2.30
Sin(EcmaRuntimeCallInfo * argv)703 JSTaggedValue BuiltinsMath::Sin(EcmaRuntimeCallInfo *argv)
704 {
705 ASSERT(argv);
706 BUILTINS_API_TRACE(argv->GetThread(), Math, Sin);
707 JSThread *thread = argv->GetThread();
708 [[maybe_unused]] EcmaHandleScope handleScope(thread);
709 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
710 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
711 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
712 double value = numberValue.GetNumber();
713 double result = base::NAN_VALUE;
714 // If value is NaN or -NaN, the result is NaN
715 if (std::isfinite(std::abs(value))) {
716 result = std::sin(value);
717 }
718 return GetTaggedDouble(result);
719 }
720
721 // 20.2.2.31
Sinh(EcmaRuntimeCallInfo * argv)722 JSTaggedValue BuiltinsMath::Sinh(EcmaRuntimeCallInfo *argv)
723 {
724 ASSERT(argv);
725 BUILTINS_API_TRACE(argv->GetThread(), Math, Sinh);
726 JSThread *thread = argv->GetThread();
727 [[maybe_unused]] EcmaHandleScope handleScope(thread);
728 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
729 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
730 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
731 double value = numberValue.GetNumber();
732 double result = base::NAN_VALUE;
733 // If value is NaN or -NaN, the result is NaN
734 if (!std::isnan(std::abs(value))) {
735 result = std::sinh(value);
736 }
737 return GetTaggedDouble(result);
738 }
739
740 // 20.2.2.32
Sqrt(EcmaRuntimeCallInfo * argv)741 JSTaggedValue BuiltinsMath::Sqrt(EcmaRuntimeCallInfo *argv)
742 {
743 ASSERT(argv);
744 BUILTINS_API_TRACE(argv->GetThread(), Math, Sqrt);
745 JSThread *thread = argv->GetThread();
746 [[maybe_unused]] EcmaHandleScope handleScope(thread);
747 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
748 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
749 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
750 double value = numberValue.GetNumber();
751 double result = base::NAN_VALUE;
752 // If value is negative, include -NaN and -Infinity but not -0.0, the result is NaN
753 if (std::signbit(value) && value != 0) {
754 return GetTaggedDouble(result);
755 }
756 // If value is NaN, the result is NaN
757 if (!std::isnan(value)) {
758 result = std::sqrt(value);
759 }
760 return GetTaggedDouble(result);
761 }
762
763 // 20.2.2.33
Tan(EcmaRuntimeCallInfo * argv)764 JSTaggedValue BuiltinsMath::Tan(EcmaRuntimeCallInfo *argv)
765 {
766 ASSERT(argv);
767 BUILTINS_API_TRACE(argv->GetThread(), Math, Tan);
768 JSThread *thread = argv->GetThread();
769 [[maybe_unused]] EcmaHandleScope handleScope(thread);
770 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
771 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
772 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
773 double value = numberValue.GetNumber();
774 double result = base::NAN_VALUE;
775 // If value is NaN or -NaN, +infinite, -infinite, result is NaN
776 if (std::isfinite(value)) {
777 result = std::tan(value);
778 }
779 return GetTaggedDouble(result);
780 }
781
782 // 20.2.2.34
Tanh(EcmaRuntimeCallInfo * argv)783 JSTaggedValue BuiltinsMath::Tanh(EcmaRuntimeCallInfo *argv)
784 {
785 ASSERT(argv);
786 BUILTINS_API_TRACE(argv->GetThread(), Math, Tanh);
787 JSThread *thread = argv->GetThread();
788 [[maybe_unused]] EcmaHandleScope handleScope(thread);
789 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
790 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
791 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
792 double value = numberValue.GetNumber();
793 double result = base::NAN_VALUE;
794 if (!std::isnan(std::abs(value))) {
795 result = std::tanh(value);
796 }
797 return GetTaggedDouble(result);
798 }
799
800 // 20.2.2.35
Trunc(EcmaRuntimeCallInfo * argv)801 JSTaggedValue BuiltinsMath::Trunc(EcmaRuntimeCallInfo *argv)
802 {
803 ASSERT(argv);
804 BUILTINS_API_TRACE(argv->GetThread(), Math, Trunc);
805 JSThread *thread = argv->GetThread();
806 [[maybe_unused]] EcmaHandleScope handleScope(thread);
807 JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
808 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
809 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
810 double value = numberValue.GetNumber();
811 double result = base::NAN_VALUE;
812 if (!std::isfinite(value)) {
813 // if value is +infinite, -infinite, NaN, -NaN, VRegisterTag is double
814 if (!std::isnan(std::abs(value))) {
815 // if value is +infinite, -infinite, result is value
816 result = value;
817 }
818 } else {
819 result = std::trunc(value);
820 }
821 return GetTaggedDouble(result);
822 }
823 } // namespace panda::ecmascript::builtins
824