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