1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qv4mathobject_p.h" |
5 | #include "qv4symbol_p.h" |
6 | |
7 | #include <QtCore/qdatetime.h> |
8 | #include <QtCore/qmath.h> |
9 | #include <QtCore/qrandom.h> |
10 | #include <QtCore/private/qnumeric_p.h> |
11 | #include <QtCore/qthreadstorage.h> |
12 | |
13 | #include <cmath> |
14 | |
15 | using namespace QV4; |
16 | |
17 | DEFINE_OBJECT_VTABLE(MathObject); |
18 | |
19 | void Heap::MathObject::init() |
20 | { |
21 | Object::init(); |
22 | Scope scope(internalClass->engine); |
23 | ScopedObject m(scope, this); |
24 | |
25 | m->defineReadonlyProperty(QStringLiteral("E" ), value: Value::fromDouble(M_E)); |
26 | m->defineReadonlyProperty(QStringLiteral("LN2" ), value: Value::fromDouble(M_LN2)); |
27 | m->defineReadonlyProperty(QStringLiteral("LN10" ), value: Value::fromDouble(M_LN10)); |
28 | m->defineReadonlyProperty(QStringLiteral("LOG2E" ), value: Value::fromDouble(M_LOG2E)); |
29 | m->defineReadonlyProperty(QStringLiteral("LOG10E" ), value: Value::fromDouble(M_LOG10E)); |
30 | m->defineReadonlyProperty(QStringLiteral("PI" ), value: Value::fromDouble(M_PI)); |
31 | m->defineReadonlyProperty(QStringLiteral("SQRT1_2" ), value: Value::fromDouble(M_SQRT1_2)); |
32 | m->defineReadonlyProperty(QStringLiteral("SQRT2" ), value: Value::fromDouble(M_SQRT2)); |
33 | |
34 | m->defineDefaultProperty(QStringLiteral("abs" ), code: QV4::MathObject::method_abs, argumentCount: 1); |
35 | m->defineDefaultProperty(QStringLiteral("acos" ), code: QV4::MathObject::method_acos, argumentCount: 1); |
36 | m->defineDefaultProperty(QStringLiteral("acosh" ), code: QV4::MathObject::method_acosh, argumentCount: 1); |
37 | m->defineDefaultProperty(QStringLiteral("asin" ), code: QV4::MathObject::method_asin, argumentCount: 1); |
38 | m->defineDefaultProperty(QStringLiteral("asinh" ), code: QV4::MathObject::method_asinh, argumentCount: 1); |
39 | m->defineDefaultProperty(QStringLiteral("atan" ), code: QV4::MathObject::method_atan, argumentCount: 1); |
40 | m->defineDefaultProperty(QStringLiteral("atanh" ), code: QV4::MathObject::method_atanh, argumentCount: 1); |
41 | m->defineDefaultProperty(QStringLiteral("atan2" ), code: QV4::MathObject::method_atan2, argumentCount: 2); |
42 | m->defineDefaultProperty(QStringLiteral("cbrt" ), code: QV4::MathObject::method_cbrt, argumentCount: 1); |
43 | m->defineDefaultProperty(QStringLiteral("ceil" ), code: QV4::MathObject::method_ceil, argumentCount: 1); |
44 | m->defineDefaultProperty(QStringLiteral("clz32" ), code: QV4::MathObject::method_clz32, argumentCount: 1); |
45 | m->defineDefaultProperty(QStringLiteral("cos" ), code: QV4::MathObject::method_cos, argumentCount: 1); |
46 | m->defineDefaultProperty(QStringLiteral("cosh" ), code: QV4::MathObject::method_cosh, argumentCount: 1); |
47 | m->defineDefaultProperty(QStringLiteral("exp" ), code: QV4::MathObject::method_exp, argumentCount: 1); |
48 | m->defineDefaultProperty(QStringLiteral("expm1" ), code: QV4::MathObject::method_expm1, argumentCount: 1); |
49 | m->defineDefaultProperty(QStringLiteral("floor" ), code: QV4::MathObject::method_floor, argumentCount: 1); |
50 | m->defineDefaultProperty(QStringLiteral("fround" ), code: QV4::MathObject::method_fround, argumentCount: 1); |
51 | m->defineDefaultProperty(QStringLiteral("hypot" ), code: QV4::MathObject::method_hypot, argumentCount: 2); |
52 | m->defineDefaultProperty(QStringLiteral("imul" ), code: QV4::MathObject::method_imul, argumentCount: 2); |
53 | m->defineDefaultProperty(QStringLiteral("log" ), code: QV4::MathObject::method_log, argumentCount: 1); |
54 | m->defineDefaultProperty(QStringLiteral("log10" ), code: QV4::MathObject::method_log10, argumentCount: 1); |
55 | m->defineDefaultProperty(QStringLiteral("log1p" ), code: QV4::MathObject::method_log1p, argumentCount: 1); |
56 | m->defineDefaultProperty(QStringLiteral("log2" ), code: QV4::MathObject::method_log2, argumentCount: 1); |
57 | m->defineDefaultProperty(QStringLiteral("max" ), code: QV4::MathObject::method_max, argumentCount: 2); |
58 | m->defineDefaultProperty(QStringLiteral("min" ), code: QV4::MathObject::method_min, argumentCount: 2); |
59 | m->defineDefaultProperty(QStringLiteral("pow" ), code: QV4::MathObject::method_pow, argumentCount: 2); |
60 | m->defineDefaultProperty(QStringLiteral("random" ), code: QV4::MathObject::method_random, argumentCount: 0); |
61 | m->defineDefaultProperty(QStringLiteral("round" ), code: QV4::MathObject::method_round, argumentCount: 1); |
62 | m->defineDefaultProperty(QStringLiteral("sign" ), code: QV4::MathObject::method_sign, argumentCount: 1); |
63 | m->defineDefaultProperty(QStringLiteral("sin" ), code: QV4::MathObject::method_sin, argumentCount: 1); |
64 | m->defineDefaultProperty(QStringLiteral("sinh" ), code: QV4::MathObject::method_sinh, argumentCount: 1); |
65 | m->defineDefaultProperty(QStringLiteral("sqrt" ), code: QV4::MathObject::method_sqrt, argumentCount: 1); |
66 | m->defineDefaultProperty(QStringLiteral("tan" ), code: QV4::MathObject::method_tan, argumentCount: 1); |
67 | m->defineDefaultProperty(QStringLiteral("tanh" ), code: QV4::MathObject::method_tanh, argumentCount: 1); |
68 | m->defineDefaultProperty(QStringLiteral("trunc" ), code: QV4::MathObject::method_trunc, argumentCount: 1); |
69 | |
70 | ScopedString name(scope, scope.engine->newString(QStringLiteral("Math" ))); |
71 | m->defineReadonlyConfigurableProperty(name: scope.engine->symbol_toStringTag(), value: name); |
72 | } |
73 | |
74 | static Q_ALWAYS_INLINE double copySign(double x, double y) |
75 | { |
76 | return ::copysign(x: x, y: y); |
77 | } |
78 | |
79 | ReturnedValue MathObject::method_abs(const FunctionObject *, const Value *, const Value *argv, int argc) |
80 | { |
81 | if (!argc) |
82 | RETURN_RESULT(Encode(qt_qnan())); |
83 | |
84 | if (argv[0].isInteger()) { |
85 | int i = argv[0].integerValue(); |
86 | RETURN_RESULT(Encode(i < 0 ? - i : i)); |
87 | } |
88 | |
89 | double v = argv[0].toNumber(); |
90 | if (v == 0) // 0 | -0 |
91 | RETURN_RESULT(Encode(0)); |
92 | |
93 | RETURN_RESULT(Encode(v < 0 ? -v : v)); |
94 | } |
95 | |
96 | ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, const Value *argv, int argc) |
97 | { |
98 | double v = argc ? argv[0].toNumber() : 2; |
99 | if (v > 1) |
100 | RETURN_RESULT(Encode(qt_qnan())); |
101 | |
102 | RETURN_RESULT(Encode(std::acos(v))); |
103 | } |
104 | |
105 | ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, const Value *argv, int argc) |
106 | { |
107 | double v = argc ? argv[0].toNumber() : 2; |
108 | if (v < 1) |
109 | RETURN_RESULT(Encode(qt_qnan())); |
110 | |
111 | #ifdef Q_CC_MINGW |
112 | // Mingw has a broken std::acosh(). It returns NaN when passed Infinity. |
113 | if (std::isinf(v)) |
114 | RETURN_RESULT(Encode(v)); |
115 | #endif |
116 | RETURN_RESULT(Encode(std::acosh(v))); |
117 | } |
118 | |
119 | ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc) |
120 | { |
121 | double v = argc ? argv[0].toNumber() : 2; |
122 | if (v > 1) |
123 | RETURN_RESULT(Encode(qt_qnan())); |
124 | else |
125 | RETURN_RESULT(Encode(std::asin(v))); |
126 | } |
127 | |
128 | ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, const Value *argv, int argc) |
129 | { |
130 | double v = argc ? argv[0].toNumber() : 2; |
131 | if (v == 0.0) |
132 | RETURN_RESULT(Encode(v)); |
133 | RETURN_RESULT(Encode(std::asinh(v))); |
134 | } |
135 | |
136 | ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc) |
137 | { |
138 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
139 | if (v == 0.0) |
140 | RETURN_RESULT(Encode(v)); |
141 | else |
142 | RETURN_RESULT(Encode(std::atan(v))); |
143 | } |
144 | |
145 | ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, const Value *argv, int argc) |
146 | { |
147 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
148 | if (v == 0.0) |
149 | RETURN_RESULT(Encode(v)); |
150 | |
151 | RETURN_RESULT(Encode(std::atanh(v))); |
152 | } |
153 | |
154 | ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc) |
155 | { |
156 | double v1 = argc ? argv[0].toNumber() : qt_qnan(); |
157 | double v2 = argc > 1 ? argv[1].toNumber() : qt_qnan(); |
158 | |
159 | if ((v1 < 0) && qt_is_finite(d: v1) && qt_is_inf(d: v2) && (copySign(x: 1.0, y: v2) == 1.0)) |
160 | RETURN_RESULT(Encode(copySign(0, -1.0))); |
161 | |
162 | if ((v1 == 0.0) && (v2 == 0.0)) { |
163 | if ((copySign(x: 1.0, y: v1) == 1.0) && (copySign(x: 1.0, y: v2) == -1.0)) { |
164 | RETURN_RESULT(Encode(M_PI)); |
165 | } else if ((copySign(x: 1.0, y: v1) == -1.0) && (copySign(x: 1.0, y: v2) == -1.0)) { |
166 | RETURN_RESULT(Encode(-M_PI)); |
167 | } |
168 | } |
169 | RETURN_RESULT(Encode(std::atan2(v1, v2))); |
170 | } |
171 | |
172 | ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc) |
173 | { |
174 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
175 | RETURN_RESULT(Encode(std::cbrt(v))); // cube root |
176 | } |
177 | |
178 | ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc) |
179 | { |
180 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
181 | if (v < 0.0 && v > -1.0) |
182 | RETURN_RESULT(Encode(copySign(0, -1.0))); |
183 | else |
184 | RETURN_RESULT(Encode(std::ceil(v))); |
185 | } |
186 | |
187 | ReturnedValue MathObject::method_clz32(const FunctionObject *, const Value *, const Value *argv, int argc) |
188 | { |
189 | quint32 v = argc ? argv[0].toUInt32() : 0; |
190 | RETURN_RESULT(Encode(qint32(qCountLeadingZeroBits(v)))); |
191 | } |
192 | |
193 | ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc) |
194 | { |
195 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
196 | RETURN_RESULT(Encode(std::cos(v))); |
197 | } |
198 | |
199 | ReturnedValue MathObject::method_cosh(const FunctionObject *, const Value *, const Value *argv, int argc) |
200 | { |
201 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
202 | RETURN_RESULT(Encode(std::cosh(v))); |
203 | } |
204 | |
205 | ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc) |
206 | { |
207 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
208 | if (qt_is_inf(d: v)) { |
209 | if (copySign(x: 1.0, y: v) == -1.0) |
210 | RETURN_RESULT(Encode(0)); |
211 | else |
212 | RETURN_RESULT(Encode(qt_inf())); |
213 | } else { |
214 | RETURN_RESULT(Encode(std::exp(v))); |
215 | } |
216 | } |
217 | |
218 | ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, const Value *argv, int argc) |
219 | { |
220 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
221 | if (std::isnan(x: v) || qIsNull(d: v)) { |
222 | RETURN_RESULT(Encode(v)); |
223 | } else if (qt_is_inf(d: v)) { |
224 | if (copySign(x: 1.0, y: v) == -1.0) |
225 | RETURN_RESULT(Encode(-1.0)); |
226 | else |
227 | RETURN_RESULT(Encode(qt_inf())); |
228 | } else { |
229 | RETURN_RESULT(Encode(std::expm1(v))); |
230 | } |
231 | } |
232 | |
233 | ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc) |
234 | { |
235 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
236 | Value result = Value::fromDouble(d: std::floor(x: v)); |
237 | result.isInt32(); |
238 | RETURN_RESULT(result); |
239 | } |
240 | |
241 | ReturnedValue MathObject::method_fround(const FunctionObject *, const Value *, const Value *argv, int argc) |
242 | { |
243 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
244 | if (std::isnan(x: v) || qt_is_inf(d: v) || qIsNull(d: v)) |
245 | RETURN_RESULT(Encode(v)); |
246 | else // convert to 32-bit float using roundTiesToEven, then convert back to 64-bit double |
247 | RETURN_RESULT(Encode(double(float(v)))); |
248 | } |
249 | |
250 | ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, const Value *argv, int argc) |
251 | { |
252 | // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to |
253 | // avoid the loss of precision from overflows and underflows" (as std::hypot does). |
254 | double v = 0; |
255 | // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ... |
256 | if (argc > 0) { |
257 | QtPrivate::QHypotHelper<double> h(argv[0].toNumber()); |
258 | for (int i = 1; i < argc; i++) |
259 | h = h.add(next: argv[i].toNumber()); |
260 | v = h.result(); |
261 | } |
262 | RETURN_RESULT(Value::fromDouble(v)); |
263 | } |
264 | |
265 | ReturnedValue MathObject::method_imul(const FunctionObject *, const Value *, const Value *argv, int argc) |
266 | { |
267 | quint32 a = argc ? argv[0].toUInt32() : 0; |
268 | quint32 b = argc > 0 ? argv[1].toUInt32() : 0; |
269 | qint32 product = a * b; |
270 | RETURN_RESULT(Encode(product)); |
271 | } |
272 | |
273 | ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc) |
274 | { |
275 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
276 | if (v < 0) |
277 | RETURN_RESULT(Encode(qt_qnan())); |
278 | else |
279 | RETURN_RESULT(Encode(std::log(v))); |
280 | } |
281 | |
282 | ReturnedValue MathObject::method_log10(const FunctionObject *, const Value *, const Value *argv, int argc) |
283 | { |
284 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
285 | if (v < 0) |
286 | RETURN_RESULT(Encode(qt_qnan())); |
287 | else |
288 | RETURN_RESULT(Encode(std::log10(v))); |
289 | } |
290 | |
291 | ReturnedValue MathObject::method_log1p(const FunctionObject *, const Value *, const Value *argv, int argc) |
292 | { |
293 | #if !defined(__ANDROID__) |
294 | using std::log1p; |
295 | #endif |
296 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
297 | if (v < -1) |
298 | RETURN_RESULT(Encode(qt_qnan())); |
299 | else |
300 | RETURN_RESULT(Encode(log1p(v))); |
301 | } |
302 | |
303 | ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, const Value *argv, int argc) |
304 | { |
305 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
306 | if (v < 0) { |
307 | RETURN_RESULT(Encode(qt_qnan())); |
308 | } else { |
309 | RETURN_RESULT(Encode(std::log2(v))); |
310 | } |
311 | } |
312 | |
313 | ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc) |
314 | { |
315 | double mx = -qt_inf(); |
316 | for (int i = 0, ei = argc; i < ei; ++i) { |
317 | double x = argv[i].toNumber(); |
318 | if ((x == 0 && mx == x && copySign(x: 1.0, y: x) == 1.0) |
319 | || (x > mx) || std::isnan(x: x)) { |
320 | mx = x; |
321 | } |
322 | } |
323 | RETURN_RESULT(Encode::smallestNumber(mx)); |
324 | } |
325 | |
326 | ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, const Value *argv, int argc) |
327 | { |
328 | double mx = qt_inf(); |
329 | for (int i = 0, ei = argc; i < ei; ++i) { |
330 | double x = argv[i].toNumber(); |
331 | if ((x == 0 && mx == x && copySign(x: 1.0, y: x) == -1.0) |
332 | || (x < mx) || std::isnan(x: x)) { |
333 | mx = x; |
334 | } |
335 | } |
336 | RETURN_RESULT(Encode::smallestNumber(mx)); |
337 | } |
338 | |
339 | ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, const Value *argv, int argc) |
340 | { |
341 | double x = argc > 0 ? argv[0].toNumber() : qt_qnan(); |
342 | double y = argc > 1 ? argv[1].toNumber() : qt_qnan(); |
343 | |
344 | RETURN_RESULT(Encode(QQmlPrivate::jsExponentiate(x, y))); |
345 | } |
346 | |
347 | ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, const Value *, int) |
348 | { |
349 | RETURN_RESULT(Encode(QRandomGenerator::global()->generateDouble())); |
350 | } |
351 | |
352 | ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc) |
353 | { |
354 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
355 | if (!std::isfinite(x: v)) |
356 | RETURN_RESULT(Encode(v)); |
357 | |
358 | if (v < 0.5 && v >= -0.5) |
359 | v = std::copysign(x: 0.0, y: v); |
360 | else |
361 | v = std::floor(x: v + 0.5); |
362 | RETURN_RESULT(Encode(v)); |
363 | } |
364 | |
365 | ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc) |
366 | { |
367 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
368 | |
369 | if (std::isnan(x: v)) |
370 | RETURN_RESULT(Encode(qt_qnan())); |
371 | |
372 | if (qIsNull(d: v)) |
373 | RETURN_RESULT(Encode(v)); |
374 | |
375 | RETURN_RESULT(Encode(std::signbit(v) ? -1 : 1)); |
376 | } |
377 | |
378 | ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc) |
379 | { |
380 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
381 | if (v == 0.0) |
382 | RETURN_RESULT(Encode(v)); |
383 | else |
384 | RETURN_RESULT(Encode(std::sin(v))); |
385 | } |
386 | |
387 | ReturnedValue MathObject::method_sinh(const FunctionObject *, const Value *, const Value *argv, int argc) |
388 | { |
389 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
390 | if (v == 0.0) |
391 | RETURN_RESULT(Encode(v)); |
392 | else |
393 | RETURN_RESULT(Encode(std::sinh(v))); |
394 | } |
395 | |
396 | ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc) |
397 | { |
398 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
399 | RETURN_RESULT(Encode(std::sqrt(v))); |
400 | } |
401 | |
402 | ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, const Value *argv, int argc) |
403 | { |
404 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
405 | if (v == 0.0) |
406 | RETURN_RESULT(Encode(v)); |
407 | else |
408 | RETURN_RESULT(Encode(std::tan(v))); |
409 | } |
410 | |
411 | ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, const Value *argv, int argc) |
412 | { |
413 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
414 | if (v == 0.0) |
415 | RETURN_RESULT(Encode(v)); |
416 | else |
417 | RETURN_RESULT(Encode(std::tanh(v))); |
418 | } |
419 | |
420 | ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc) |
421 | { |
422 | double v = argc ? argv[0].toNumber() : qt_qnan(); |
423 | RETURN_RESULT(Encode(std::trunc(v))); |
424 | } |
425 | |