Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
* JS math package.
9
*/
10
11
#include "jsmath.h"
12
13
#include "mozilla/FloatingPoint.h"
14
#include "mozilla/MathAlgorithms.h"
15
#include "mozilla/MemoryReporting.h"
16
#include "mozilla/RandomNum.h"
17
#include "mozilla/Unused.h"
18
#include "mozilla/WrappingOperations.h"
19
20
#include <cmath>
21
22
#include "fdlibm.h"
23
#include "jsapi.h"
24
#include "jstypes.h"
25
26
#include "jit/InlinableNatives.h"
27
#include "js/Class.h"
28
#include "js/PropertySpec.h"
29
#include "util/Windows.h"
30
#include "vm/JSAtom.h"
31
#include "vm/JSContext.h"
32
#include "vm/Realm.h"
33
#include "vm/Time.h"
34
35
#include "vm/JSObject-inl.h"
36
37
using namespace js;
38
39
using JS::GenericNaN;
40
using JS::ToNumber;
41
using mozilla::Abs;
42
using mozilla::ExponentComponent;
43
using mozilla::FloatingPoint;
44
using mozilla::IsFinite;
45
using mozilla::IsInfinite;
46
using mozilla::IsNaN;
47
using mozilla::IsNegative;
48
using mozilla::IsNegativeZero;
49
using mozilla::Maybe;
50
using mozilla::NegativeInfinity;
51
using mozilla::NumberEqualsInt32;
52
using mozilla::NumberIsInt32;
53
using mozilla::PositiveInfinity;
54
using mozilla::WrappingMultiply;
55
56
static const JSConstDoubleSpec math_constants[] = {
57
// clang-format off
58
{"E" , M_E },
59
{"LOG2E" , M_LOG2E },
60
{"LOG10E" , M_LOG10E },
61
{"LN2" , M_LN2 },
62
{"LN10" , M_LN10 },
63
{"PI" , M_PI },
64
{"SQRT2" , M_SQRT2 },
65
{"SQRT1_2", M_SQRT1_2 },
66
{nullptr , 0 }
67
// clang-format on
68
};
69
70
using UnaryMathFunctionType = double (*)(double);
71
72
template <UnaryMathFunctionType F>
73
static bool math_function(JSContext* cx, HandleValue val,
74
MutableHandleValue res) {
75
double x;
76
if (!ToNumber(cx, val, &x)) {
77
return false;
78
}
79
80
// NB: Always stored as a double so the math function can be inlined
81
// through MMathFunction.
82
double z = F(x);
83
res.setDouble(z);
84
return true;
85
}
86
87
template <UnaryMathFunctionType F>
88
static bool math_function(JSContext* cx, unsigned argc, Value* vp) {
89
CallArgs args = CallArgsFromVp(argc, vp);
90
if (args.length() == 0) {
91
args.rval().setNaN();
92
return true;
93
}
94
95
return math_function<F>(cx, args[0], args.rval());
96
}
97
98
bool js::math_abs_handle(JSContext* cx, js::HandleValue v,
99
js::MutableHandleValue r) {
100
double x;
101
if (!ToNumber(cx, v, &x)) {
102
return false;
103
}
104
105
double z = Abs(x);
106
r.setNumber(z);
107
108
return true;
109
}
110
111
bool js::math_abs(JSContext* cx, unsigned argc, Value* vp) {
112
CallArgs args = CallArgsFromVp(argc, vp);
113
114
if (args.length() == 0) {
115
args.rval().setNaN();
116
return true;
117
}
118
119
return math_abs_handle(cx, args[0], args.rval());
120
}
121
122
double js::math_acos_impl(double x) {
123
AutoUnsafeCallWithABI unsafe;
124
return fdlibm::acos(x);
125
}
126
127
bool js::math_acos(JSContext* cx, unsigned argc, Value* vp) {
128
return math_function<math_acos_impl>(cx, argc, vp);
129
}
130
131
double js::math_asin_impl(double x) {
132
AutoUnsafeCallWithABI unsafe;
133
return fdlibm::asin(x);
134
}
135
136
bool js::math_asin(JSContext* cx, unsigned argc, Value* vp) {
137
return math_function<math_asin_impl>(cx, argc, vp);
138
}
139
140
double js::math_atan_impl(double x) {
141
AutoUnsafeCallWithABI unsafe;
142
return fdlibm::atan(x);
143
}
144
145
bool js::math_atan(JSContext* cx, unsigned argc, Value* vp) {
146
return math_function<math_atan_impl>(cx, argc, vp);
147
}
148
149
double js::ecmaAtan2(double y, double x) {
150
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
151
return fdlibm::atan2(y, x);
152
}
153
154
bool js::math_atan2_handle(JSContext* cx, HandleValue y, HandleValue x,
155
MutableHandleValue res) {
156
double dy;
157
if (!ToNumber(cx, y, &dy)) {
158
return false;
159
}
160
161
double dx;
162
if (!ToNumber(cx, x, &dx)) {
163
return false;
164
}
165
166
double z = ecmaAtan2(dy, dx);
167
res.setDouble(z);
168
return true;
169
}
170
171
bool js::math_atan2(JSContext* cx, unsigned argc, Value* vp) {
172
CallArgs args = CallArgsFromVp(argc, vp);
173
174
return math_atan2_handle(cx, args.get(0), args.get(1), args.rval());
175
}
176
177
double js::math_ceil_impl(double x) {
178
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
179
return fdlibm::ceil(x);
180
}
181
182
bool js::math_ceil_handle(JSContext* cx, HandleValue v,
183
MutableHandleValue res) {
184
double d;
185
if (!ToNumber(cx, v, &d)) return false;
186
187
double result = math_ceil_impl(d);
188
res.setNumber(result);
189
return true;
190
}
191
192
bool js::math_ceil(JSContext* cx, unsigned argc, Value* vp) {
193
CallArgs args = CallArgsFromVp(argc, vp);
194
195
if (args.length() == 0) {
196
args.rval().setNaN();
197
return true;
198
}
199
200
return math_ceil_handle(cx, args[0], args.rval());
201
}
202
203
bool js::math_clz32(JSContext* cx, unsigned argc, Value* vp) {
204
CallArgs args = CallArgsFromVp(argc, vp);
205
206
if (args.length() == 0) {
207
args.rval().setInt32(32);
208
return true;
209
}
210
211
uint32_t n;
212
if (!ToUint32(cx, args[0], &n)) {
213
return false;
214
}
215
216
if (n == 0) {
217
args.rval().setInt32(32);
218
return true;
219
}
220
221
args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
222
return true;
223
}
224
225
double js::math_cos_impl(double x) {
226
AutoUnsafeCallWithABI unsafe;
227
return cos(x);
228
}
229
230
bool js::math_cos(JSContext* cx, unsigned argc, Value* vp) {
231
return math_function<math_cos_impl>(cx, argc, vp);
232
}
233
234
double js::math_exp_impl(double x) {
235
AutoUnsafeCallWithABI unsafe;
236
return fdlibm::exp(x);
237
}
238
239
bool js::math_exp(JSContext* cx, unsigned argc, Value* vp) {
240
return math_function<math_exp_impl>(cx, argc, vp);
241
}
242
243
double js::math_floor_impl(double x) {
244
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
245
return fdlibm::floor(x);
246
}
247
248
bool js::math_floor_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
249
double d;
250
if (!ToNumber(cx, v, &d)) {
251
return false;
252
}
253
254
double z = math_floor_impl(d);
255
r.setNumber(z);
256
257
return true;
258
}
259
260
bool js::math_floor(JSContext* cx, unsigned argc, Value* vp) {
261
CallArgs args = CallArgsFromVp(argc, vp);
262
263
if (args.length() == 0) {
264
args.rval().setNaN();
265
return true;
266
}
267
268
return math_floor_handle(cx, args[0], args.rval());
269
}
270
271
bool js::math_imul_handle(JSContext* cx, HandleValue lhs, HandleValue rhs,
272
MutableHandleValue res) {
273
int32_t a = 0, b = 0;
274
if (!lhs.isUndefined() && !ToInt32(cx, lhs, &a)) {
275
return false;
276
}
277
if (!rhs.isUndefined() && !ToInt32(cx, rhs, &b)) {
278
return false;
279
}
280
281
res.setInt32(WrappingMultiply(a, b));
282
return true;
283
}
284
285
bool js::math_imul(JSContext* cx, unsigned argc, Value* vp) {
286
CallArgs args = CallArgsFromVp(argc, vp);
287
288
return math_imul_handle(cx, args.get(0), args.get(1), args.rval());
289
}
290
291
// Implements Math.fround (20.2.2.16) up to step 3
292
bool js::RoundFloat32(JSContext* cx, HandleValue v, float* out) {
293
double d;
294
bool success = ToNumber(cx, v, &d);
295
*out = static_cast<float>(d);
296
return success;
297
}
298
299
bool js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res) {
300
float f;
301
if (!RoundFloat32(cx, arg, &f)) {
302
return false;
303
}
304
305
res.setDouble(static_cast<double>(f));
306
return true;
307
}
308
309
bool js::math_fround(JSContext* cx, unsigned argc, Value* vp) {
310
CallArgs args = CallArgsFromVp(argc, vp);
311
312
if (args.length() == 0) {
313
args.rval().setNaN();
314
return true;
315
}
316
317
return RoundFloat32(cx, args[0], args.rval());
318
}
319
320
double js::math_log_impl(double x) {
321
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
322
return fdlibm::log(x);
323
}
324
325
bool js::math_log_handle(JSContext* cx, HandleValue val,
326
MutableHandleValue res) {
327
return math_function<math_log_impl>(cx, val, res);
328
}
329
330
bool js::math_log(JSContext* cx, unsigned argc, Value* vp) {
331
return math_function<math_log_impl>(cx, argc, vp);
332
}
333
334
double js::math_max_impl(double x, double y) {
335
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
336
337
// Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
338
if (x > y || IsNaN(x) || (x == y && IsNegative(y))) {
339
return x;
340
}
341
return y;
342
}
343
344
bool js::math_max(JSContext* cx, unsigned argc, Value* vp) {
345
CallArgs args = CallArgsFromVp(argc, vp);
346
347
double maxval = NegativeInfinity<double>();
348
for (unsigned i = 0; i < args.length(); i++) {
349
double x;
350
if (!ToNumber(cx, args[i], &x)) {
351
return false;
352
}
353
maxval = math_max_impl(x, maxval);
354
}
355
args.rval().setNumber(maxval);
356
return true;
357
}
358
359
double js::math_min_impl(double x, double y) {
360
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
361
362
// Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
363
if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x))) {
364
return x;
365
}
366
return y;
367
}
368
369
bool js::math_min(JSContext* cx, unsigned argc, Value* vp) {
370
CallArgs args = CallArgsFromVp(argc, vp);
371
372
double minval = PositiveInfinity<double>();
373
for (unsigned i = 0; i < args.length(); i++) {
374
double x;
375
if (!ToNumber(cx, args[i], &x)) {
376
return false;
377
}
378
minval = math_min_impl(x, minval);
379
}
380
args.rval().setNumber(minval);
381
return true;
382
}
383
384
bool js::minmax_impl(JSContext* cx, bool max, HandleValue a, HandleValue b,
385
MutableHandleValue res) {
386
double x, y;
387
388
if (!ToNumber(cx, a, &x)) {
389
return false;
390
}
391
if (!ToNumber(cx, b, &y)) {
392
return false;
393
}
394
395
if (max) {
396
res.setNumber(math_max_impl(x, y));
397
} else {
398
res.setNumber(math_min_impl(x, y));
399
}
400
401
return true;
402
}
403
404
double js::powi(double x, int32_t y) {
405
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
406
uint32_t n = Abs(y);
407
double m = x;
408
double p = 1;
409
while (true) {
410
if ((n & 1) != 0) p *= m;
411
n >>= 1;
412
if (n == 0) {
413
if (y < 0) {
414
// Unfortunately, we have to be careful when p has reached
415
// infinity in the computation, because sometimes the higher
416
// internal precision in the pow() implementation would have
417
// given us a finite p. This happens very rarely.
418
419
double result = 1.0 / p;
420
return (result == 0 && IsInfinite(p))
421
? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
422
: result;
423
}
424
425
return p;
426
}
427
m *= m;
428
}
429
}
430
431
double js::ecmaPow(double x, double y) {
432
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
433
434
/*
435
* Use powi if the exponent is an integer-valued double. We don't have to
436
* check for NaN since a comparison with NaN is always false.
437
*/
438
int32_t yi;
439
if (NumberEqualsInt32(y, &yi)) {
440
return powi(x, yi);
441
}
442
443
/*
444
* Because C99 and ECMA specify different behavior for pow(),
445
* we need to wrap the libm call to make it ECMA compliant.
446
*/
447
if (!IsFinite(y) && (x == 1.0 || x == -1.0)) {
448
return GenericNaN();
449
}
450
451
/* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
452
if (y == 0) {
453
return 1;
454
}
455
456
/*
457
* Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
458
* when x = -0.0, so we have to guard for this.
459
*/
460
if (IsFinite(x) && x != 0.0) {
461
if (y == 0.5) {
462
return sqrt(x);
463
}
464
if (y == -0.5) {
465
return 1.0 / sqrt(x);
466
}
467
}
468
return pow(x, y);
469
}
470
471
bool js::math_pow(JSContext* cx, unsigned argc, Value* vp) {
472
CallArgs args = CallArgsFromVp(argc, vp);
473
474
double x;
475
if (!ToNumber(cx, args.get(0), &x)) {
476
return false;
477
}
478
479
double y;
480
if (!ToNumber(cx, args.get(1), &y)) {
481
return false;
482
}
483
484
double z = ecmaPow(x, y);
485
args.rval().setNumber(z);
486
return true;
487
}
488
489
uint64_t js::GenerateRandomSeed() {
490
Maybe<uint64_t> maybeSeed = mozilla::RandomUint64();
491
492
return maybeSeed.valueOrFrom([] {
493
// Use PRMJ_Now() in case we couldn't read random bits from the OS.
494
uint64_t timestamp = PRMJ_Now();
495
return timestamp ^ (timestamp << 32);
496
});
497
}
498
499
void js::GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed) {
500
// XorShift128PlusRNG must be initialized with a non-zero seed.
501
do {
502
seed[0] = GenerateRandomSeed();
503
seed[1] = GenerateRandomSeed();
504
} while (seed[0] == 0 && seed[1] == 0);
505
}
506
507
mozilla::non_crypto::XorShift128PlusRNG&
508
Realm::getOrCreateRandomNumberGenerator() {
509
if (randomNumberGenerator_.isNothing()) {
510
mozilla::Array<uint64_t, 2> seed;
511
GenerateXorShift128PlusSeed(seed);
512
randomNumberGenerator_.emplace(seed[0], seed[1]);
513
}
514
515
return randomNumberGenerator_.ref();
516
}
517
518
double js::math_random_impl(JSContext* cx) {
519
return cx->realm()->getOrCreateRandomNumberGenerator().nextDouble();
520
}
521
522
bool js::math_random(JSContext* cx, unsigned argc, Value* vp) {
523
CallArgs args = CallArgsFromVp(argc, vp);
524
args.rval().setDouble(math_random_impl(cx));
525
return true;
526
}
527
528
bool js::math_round_handle(JSContext* cx, HandleValue arg,
529
MutableHandleValue res) {
530
double d;
531
if (!ToNumber(cx, arg, &d)) {
532
return false;
533
}
534
535
d = math_round_impl(d);
536
res.setNumber(d);
537
return true;
538
}
539
540
template <typename T>
541
T js::GetBiggestNumberLessThan(T x) {
542
MOZ_ASSERT(!IsNegative(x));
543
MOZ_ASSERT(IsFinite(x));
544
using Bits = typename mozilla::FloatingPoint<T>::Bits;
545
Bits bits = mozilla::BitwiseCast<Bits>(x);
546
MOZ_ASSERT(bits > 0, "will underflow");
547
return mozilla::BitwiseCast<T>(bits - 1);
548
}
549
550
template double js::GetBiggestNumberLessThan<>(double x);
551
template float js::GetBiggestNumberLessThan<>(float x);
552
553
double js::math_round_impl(double x) {
554
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
555
556
int32_t ignored;
557
if (NumberEqualsInt32(x, &ignored)) {
558
return x;
559
}
560
561
/* Some numbers are so big that adding 0.5 would give the wrong number. */
562
if (ExponentComponent(x) >=
563
int_fast16_t(FloatingPoint<double>::kExponentShift)) {
564
return x;
565
}
566
567
double add = (x >= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
568
return std::copysign(fdlibm::floor(x + add), x);
569
}
570
571
float js::math_roundf_impl(float x) {
572
AutoUnsafeCallWithABI unsafe;
573
574
int32_t ignored;
575
if (NumberEqualsInt32(x, &ignored)) {
576
return x;
577
}
578
579
/* Some numbers are so big that adding 0.5 would give the wrong number. */
580
if (ExponentComponent(x) >=
581
int_fast16_t(FloatingPoint<float>::kExponentShift)) {
582
return x;
583
}
584
585
float add = (x >= 0) ? GetBiggestNumberLessThan(0.5f) : 0.5f;
586
return std::copysign(fdlibm::floorf(x + add), x);
587
}
588
589
bool /* ES5 15.8.2.15. */
590
js::math_round(JSContext* cx, unsigned argc, Value* vp) {
591
CallArgs args = CallArgsFromVp(argc, vp);
592
593
if (args.length() == 0) {
594
args.rval().setNaN();
595
return true;
596
}
597
598
return math_round_handle(cx, args[0], args.rval());
599
}
600
601
double js::math_sin_impl(double x) {
602
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
603
return sin(x);
604
}
605
606
bool js::math_sin_handle(JSContext* cx, HandleValue val,
607
MutableHandleValue res) {
608
return math_function<math_sin_impl>(cx, val, res);
609
}
610
611
bool js::math_sin(JSContext* cx, unsigned argc, Value* vp) {
612
return math_function<math_sin_impl>(cx, argc, vp);
613
}
614
615
double js::math_sqrt_impl(double x) {
616
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
617
return sqrt(x);
618
}
619
620
bool js::math_sqrt_handle(JSContext* cx, HandleValue number,
621
MutableHandleValue result) {
622
return math_function<math_sqrt_impl>(cx, number, result);
623
}
624
625
bool js::math_sqrt(JSContext* cx, unsigned argc, Value* vp) {
626
return math_function<math_sqrt_impl>(cx, argc, vp);
627
}
628
629
double js::math_tan_impl(double x) {
630
AutoUnsafeCallWithABI unsafe;
631
return tan(x);
632
}
633
634
bool js::math_tan(JSContext* cx, unsigned argc, Value* vp) {
635
return math_function<math_tan_impl>(cx, argc, vp);
636
}
637
638
double js::math_log10_impl(double x) {
639
AutoUnsafeCallWithABI unsafe;
640
return fdlibm::log10(x);
641
}
642
643
bool js::math_log10(JSContext* cx, unsigned argc, Value* vp) {
644
return math_function<math_log10_impl>(cx, argc, vp);
645
}
646
647
double js::math_log2_impl(double x) {
648
AutoUnsafeCallWithABI unsafe;
649
return fdlibm::log2(x);
650
}
651
652
bool js::math_log2(JSContext* cx, unsigned argc, Value* vp) {
653
return math_function<math_log2_impl>(cx, argc, vp);
654
}
655
656
double js::math_log1p_impl(double x) {
657
AutoUnsafeCallWithABI unsafe;
658
return fdlibm::log1p(x);
659
}
660
661
bool js::math_log1p(JSContext* cx, unsigned argc, Value* vp) {
662
return math_function<math_log1p_impl>(cx, argc, vp);
663
}
664
665
double js::math_expm1_impl(double x) {
666
AutoUnsafeCallWithABI unsafe;
667
return fdlibm::expm1(x);
668
}
669
670
bool js::math_expm1(JSContext* cx, unsigned argc, Value* vp) {
671
return math_function<math_expm1_impl>(cx, argc, vp);
672
}
673
674
double js::math_cosh_impl(double x) {
675
AutoUnsafeCallWithABI unsafe;
676
return fdlibm::cosh(x);
677
}
678
679
bool js::math_cosh(JSContext* cx, unsigned argc, Value* vp) {
680
return math_function<math_cosh_impl>(cx, argc, vp);
681
}
682
683
double js::math_sinh_impl(double x) {
684
AutoUnsafeCallWithABI unsafe;
685
return fdlibm::sinh(x);
686
}
687
688
bool js::math_sinh(JSContext* cx, unsigned argc, Value* vp) {
689
return math_function<math_sinh_impl>(cx, argc, vp);
690
}
691
692
double js::math_tanh_impl(double x) {
693
AutoUnsafeCallWithABI unsafe;
694
return fdlibm::tanh(x);
695
}
696
697
bool js::math_tanh(JSContext* cx, unsigned argc, Value* vp) {
698
return math_function<math_tanh_impl>(cx, argc, vp);
699
}
700
701
double js::math_acosh_impl(double x) {
702
AutoUnsafeCallWithABI unsafe;
703
return fdlibm::acosh(x);
704
}
705
706
bool js::math_acosh(JSContext* cx, unsigned argc, Value* vp) {
707
return math_function<math_acosh_impl>(cx, argc, vp);
708
}
709
710
double js::math_asinh_impl(double x) {
711
AutoUnsafeCallWithABI unsafe;
712
return fdlibm::asinh(x);
713
}
714
715
bool js::math_asinh(JSContext* cx, unsigned argc, Value* vp) {
716
return math_function<math_asinh_impl>(cx, argc, vp);
717
}
718
719
double js::math_atanh_impl(double x) {
720
AutoUnsafeCallWithABI unsafe;
721
return fdlibm::atanh(x);
722
}
723
724
bool js::math_atanh(JSContext* cx, unsigned argc, Value* vp) {
725
return math_function<math_atanh_impl>(cx, argc, vp);
726
}
727
728
double js::ecmaHypot(double x, double y) {
729
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
730
return fdlibm::hypot(x, y);
731
}
732
733
static inline void hypot_step(double& scale, double& sumsq, double x) {
734
double xabs = mozilla::Abs(x);
735
if (scale < xabs) {
736
sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
737
scale = xabs;
738
} else if (scale != 0) {
739
sumsq += (xabs / scale) * (xabs / scale);
740
}
741
}
742
743
double js::hypot4(double x, double y, double z, double w) {
744
AutoUnsafeCallWithABI unsafe;
745
746
// Check for infinities or NaNs so that we can return immediately.
747
if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y) ||
748
mozilla::IsInfinite(z) || mozilla::IsInfinite(w)) {
749
return mozilla::PositiveInfinity<double>();
750
}
751
752
if (mozilla::IsNaN(x) || mozilla::IsNaN(y) || mozilla::IsNaN(z) ||
753
mozilla::IsNaN(w)) {
754
return GenericNaN();
755
}
756
757
double scale = 0;
758
double sumsq = 1;
759
760
hypot_step(scale, sumsq, x);
761
hypot_step(scale, sumsq, y);
762
hypot_step(scale, sumsq, z);
763
hypot_step(scale, sumsq, w);
764
765
return scale * sqrt(sumsq);
766
}
767
768
double js::hypot3(double x, double y, double z) {
769
AutoUnsafeCallWithABI unsafe;
770
return hypot4(x, y, z, 0.0);
771
}
772
773
bool js::math_hypot(JSContext* cx, unsigned argc, Value* vp) {
774
CallArgs args = CallArgsFromVp(argc, vp);
775
return math_hypot_handle(cx, args, args.rval());
776
}
777
778
bool js::math_hypot_handle(JSContext* cx, HandleValueArray args,
779
MutableHandleValue res) {
780
// IonMonkey calls the ecmaHypot function directly if two arguments are
781
// given. Do that here as well to get the same results.
782
if (args.length() == 2) {
783
double x, y;
784
if (!ToNumber(cx, args[0], &x)) {
785
return false;
786
}
787
if (!ToNumber(cx, args[1], &y)) {
788
return false;
789
}
790
791
double result = ecmaHypot(x, y);
792
res.setDouble(result);
793
return true;
794
}
795
796
bool isInfinite = false;
797
bool isNaN = false;
798
799
double scale = 0;
800
double sumsq = 1;
801
802
for (unsigned i = 0; i < args.length(); i++) {
803
double x;
804
if (!ToNumber(cx, args[i], &x)) {
805
return false;
806
}
807
808
isInfinite |= mozilla::IsInfinite(x);
809
isNaN |= mozilla::IsNaN(x);
810
if (isInfinite || isNaN) {
811
continue;
812
}
813
814
hypot_step(scale, sumsq, x);
815
}
816
817
double result = isInfinite ? PositiveInfinity<double>()
818
: isNaN ? GenericNaN() : scale * sqrt(sumsq);
819
res.setDouble(result);
820
return true;
821
}
822
823
double js::math_trunc_impl(double x) {
824
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
825
return fdlibm::trunc(x);
826
}
827
828
float js::math_truncf_impl(float x) {
829
AutoUnsafeCallWithABI unsafe;
830
return fdlibm::truncf(x);
831
}
832
833
bool js::math_trunc_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
834
double x;
835
if (!ToNumber(cx, v, &x)) {
836
return false;
837
}
838
839
r.setNumber(math_trunc_impl(x));
840
return true;
841
}
842
843
bool js::math_trunc(JSContext* cx, unsigned argc, Value* vp) {
844
CallArgs args = CallArgsFromVp(argc, vp);
845
if (args.length() == 0) {
846
args.rval().setNaN();
847
return true;
848
}
849
850
return math_trunc_handle(cx, args[0], args.rval());
851
}
852
853
double js::math_sign_impl(double x) {
854
AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
855
856
if (mozilla::IsNaN(x)) {
857
return GenericNaN();
858
}
859
860
return x == 0 ? x : x < 0 ? -1 : 1;
861
}
862
863
bool js::math_sign_handle(JSContext* cx, HandleValue v, MutableHandleValue r) {
864
double x;
865
if (!ToNumber(cx, v, &x)) {
866
return false;
867
}
868
869
r.setNumber(math_sign_impl(x));
870
return true;
871
}
872
873
bool js::math_sign(JSContext* cx, unsigned argc, Value* vp) {
874
CallArgs args = CallArgsFromVp(argc, vp);
875
if (args.length() == 0) {
876
args.rval().setNaN();
877
return true;
878
}
879
880
return math_sign_handle(cx, args[0], args.rval());
881
}
882
883
double js::math_cbrt_impl(double x) {
884
AutoUnsafeCallWithABI unsafe;
885
return fdlibm::cbrt(x);
886
}
887
888
bool js::math_cbrt(JSContext* cx, unsigned argc, Value* vp) {
889
return math_function<math_cbrt_impl>(cx, argc, vp);
890
}
891
892
static bool math_toSource(JSContext* cx, unsigned argc, Value* vp) {
893
CallArgs args = CallArgsFromVp(argc, vp);
894
args.rval().setString(cx->names().Math);
895
return true;
896
}
897
898
static const JSFunctionSpec math_static_methods[] = {
899
JS_FN(js_toSource_str, math_toSource, 0, 0),
900
JS_INLINABLE_FN("abs", math_abs, 1, 0, MathAbs),
901
JS_INLINABLE_FN("acos", math_acos, 1, 0, MathACos),
902
JS_INLINABLE_FN("asin", math_asin, 1, 0, MathASin),
903
JS_INLINABLE_FN("atan", math_atan, 1, 0, MathATan),
904
JS_INLINABLE_FN("atan2", math_atan2, 2, 0, MathATan2),
905
JS_INLINABLE_FN("ceil", math_ceil, 1, 0, MathCeil),
906
JS_INLINABLE_FN("clz32", math_clz32, 1, 0, MathClz32),
907
JS_INLINABLE_FN("cos", math_cos, 1, 0, MathCos),
908
JS_INLINABLE_FN("exp", math_exp, 1, 0, MathExp),
909
JS_INLINABLE_FN("floor", math_floor, 1, 0, MathFloor),
910
JS_INLINABLE_FN("imul", math_imul, 2, 0, MathImul),
911
JS_INLINABLE_FN("fround", math_fround, 1, 0, MathFRound),
912
JS_INLINABLE_FN("log", math_log, 1, 0, MathLog),
913
JS_INLINABLE_FN("max", math_max, 2, 0, MathMax),
914
JS_INLINABLE_FN("min", math_min, 2, 0, MathMin),
915
JS_INLINABLE_FN("pow", math_pow, 2, 0, MathPow),
916
JS_INLINABLE_FN("random", math_random, 0, 0, MathRandom),
917
JS_INLINABLE_FN("round", math_round, 1, 0, MathRound),
918
JS_INLINABLE_FN("sin", math_sin, 1, 0, MathSin),
919
JS_INLINABLE_FN("sqrt", math_sqrt, 1, 0, MathSqrt),
920
JS_INLINABLE_FN("tan", math_tan, 1, 0, MathTan),
921
JS_INLINABLE_FN("log10", math_log10, 1, 0, MathLog10),
922
JS_INLINABLE_FN("log2", math_log2, 1, 0, MathLog2),
923
JS_INLINABLE_FN("log1p", math_log1p, 1, 0, MathLog1P),
924
JS_INLINABLE_FN("expm1", math_expm1, 1, 0, MathExpM1),
925
JS_INLINABLE_FN("cosh", math_cosh, 1, 0, MathCosH),
926
JS_INLINABLE_FN("sinh", math_sinh, 1, 0, MathSinH),
927
JS_INLINABLE_FN("tanh", math_tanh, 1, 0, MathTanH),
928
JS_INLINABLE_FN("acosh", math_acosh, 1, 0, MathACosH),
929
JS_INLINABLE_FN("asinh", math_asinh, 1, 0, MathASinH),
930
JS_INLINABLE_FN("atanh", math_atanh, 1, 0, MathATanH),
931
JS_INLINABLE_FN("hypot", math_hypot, 2, 0, MathHypot),
932
JS_INLINABLE_FN("trunc", math_trunc, 1, 0, MathTrunc),
933
JS_INLINABLE_FN("sign", math_sign, 1, 0, MathSign),
934
JS_INLINABLE_FN("cbrt", math_cbrt, 1, 0, MathCbrt),
935
JS_FS_END};
936
937
static const JSPropertySpec math_static_properties[] = {
938
JS_STRING_SYM_PS(toStringTag, "Math", JSPROP_READONLY), JS_PS_END};
939
940
static JSObject* CreateMathObject(JSContext* cx, JSProtoKey key) {
941
Handle<GlobalObject*> global = cx->global();
942
RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
943
if (!proto) {
944
return nullptr;
945
}
946
return NewObjectWithGivenProto(cx, &MathClass, proto, SingletonObject);
947
}
948
949
static bool MathClassFinish(JSContext* cx, HandleObject ctor,
950
HandleObject proto) {
951
return JS_DefineConstDoubles(cx, ctor, math_constants);
952
}
953
954
static const ClassSpec MathClassSpec = {
955
CreateMathObject, nullptr, math_static_methods, math_static_properties,
956
nullptr, nullptr, MathClassFinish};
957
958
const JSClass js::MathClass = {js_Math_str,
959
JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
960
JS_NULL_CLASS_OPS, &MathClassSpec};