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