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 "qv4numberobject_p.h"
5#include "qv4runtime_p.h"
6
7#include <QtCore/qnumeric.h>
8#include <QtCore/qmath.h>
9#include <QtCore/QDebug>
10#include <cassert>
11#include <limits>
12
13using namespace QV4;
14
15DEFINE_OBJECT_VTABLE(NumberCtor);
16DEFINE_OBJECT_VTABLE(NumberObject);
17
18namespace {
19struct NumberLocaleHolder : public NumberLocale
20{
21 NumberLocaleHolder() {}
22};
23
24Q_GLOBAL_STATIC(NumberLocaleHolder, numberLocaleHolder)
25} // namespace
26
27NumberLocale::NumberLocale() : QLocale(QLocale::C),
28 // -128 means shortest string that can accurately represent the number.
29 defaultDoublePrecision(0xffffff80)
30{
31 setNumberOptions(QLocale::OmitGroupSeparator |
32 QLocale::OmitLeadingZeroInExponent |
33 QLocale::IncludeTrailingZeroesAfterDot);
34}
35
36const NumberLocale *NumberLocale::instance()
37{
38 return numberLocaleHolder();
39}
40
41void Heap::NumberCtor::init(QV4::ExecutionEngine *engine)
42{
43 Heap::FunctionObject::init(engine, QStringLiteral("Number"));
44}
45
46ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
47{
48 auto v4 = f->engine();
49 double dbl = argc ? argv[0].toNumber() : 0.;
50
51 ReturnedValue o = Encode(f->engine()->newNumberObject(value: dbl));
52 if (!newTarget)
53 return o;
54 Scope scope(v4);
55 ScopedObject obj(scope, o);
56 obj->setProtoFromNewTarget(newTarget);
57 return obj->asReturnedValue();
58}
59
60ReturnedValue NumberCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc)
61{
62 double dbl = argc ? argv[0].toNumber() : 0.;
63 return Encode(dbl);
64}
65
66void NumberPrototype::init(ExecutionEngine *engine, Object *ctor)
67{
68 Scope scope(engine);
69 ScopedObject o(scope);
70 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
71 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 1));
72
73 ctor->defineReadonlyProperty(QStringLiteral("NaN"), value: Value::fromDouble(d: qt_qnan()));
74 ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), value: Value::fromDouble(d: -qInf()));
75 ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), value: Value::fromDouble(d: qInf()));
76 ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), value: Value::fromDouble(d: 1.7976931348623158e+308));
77 ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), value: Value::fromDouble(d: std::numeric_limits<double>::epsilon()));
78 ctor->defineReadonlyProperty(QStringLiteral("MAX_SAFE_INTEGER"), value: Value::fromDouble(d: 9007199254740991));
79 ctor->defineReadonlyProperty(QStringLiteral("MIN_SAFE_INTEGER"), value: Value::fromDouble(d: -9007199254740991));
80
81QT_WARNING_PUSH
82QT_WARNING_DISABLE_INTEL(239)
83 ctor->defineReadonlyProperty(QStringLiteral("MIN_VALUE"), value: Value::fromDouble(d: 5e-324));
84QT_WARNING_POP
85
86 ctor->defineDefaultProperty(QStringLiteral("isFinite"), code: method_isFinite, argumentCount: 1);
87 ctor->defineDefaultProperty(QStringLiteral("isInteger"), code: method_isInteger, argumentCount: 1);
88 ctor->defineDefaultProperty(QStringLiteral("isSafeInteger"), code: method_isSafeInteger, argumentCount: 1);
89 ctor->defineDefaultProperty(QStringLiteral("isNaN"), code: method_isNaN, argumentCount: 1);
90
91 defineDefaultProperty(QStringLiteral("constructor"), value: (o = ctor));
92 defineDefaultProperty(name: engine->id_toString(), code: method_toString, argumentCount: 1);
93 defineDefaultProperty(name: engine->id_toLocaleString(), code: method_toLocaleString);
94 defineDefaultProperty(name: engine->id_valueOf(), code: method_valueOf);
95 defineDefaultProperty(QStringLiteral("toFixed"), code: method_toFixed, argumentCount: 1);
96 defineDefaultProperty(QStringLiteral("toExponential"), code: method_toExponential, argumentCount: 1);
97 defineDefaultProperty(QStringLiteral("toPrecision"), code: method_toPrecision, argumentCount: 1);
98}
99
100inline ReturnedValue thisNumberValue(ExecutionEngine *v4, const Value *thisObject)
101{
102 if (thisObject->isNumber())
103 return thisObject->asReturnedValue();
104 const NumberObject *n = thisObject->as<NumberObject>();
105 if (!n) {
106 v4->throwTypeError();
107 return Encode::undefined();
108 }
109 return Encode(n->value());
110}
111
112inline double thisNumber(ExecutionEngine *engine, const Value *thisObject)
113{
114 if (thisObject->isNumber())
115 return thisObject->asDouble();
116 const NumberObject *n = thisObject->as<NumberObject>();
117 if (!n) {
118 engine->throwTypeError();
119 return 0;
120 }
121 return n->value();
122}
123
124ReturnedValue NumberPrototype::method_isFinite(const FunctionObject *, const Value *, const Value *argv, int argc)
125{
126 if (!argc || !argv[0].isNumber())
127 return Encode(false);
128
129 double v = argv[0].toNumber();
130 return Encode(!std::isnan(x: v) && !qt_is_inf(d: v));
131}
132
133ReturnedValue NumberPrototype::method_isInteger(const FunctionObject *, const Value *, const Value *argv, int argc)
134{
135 if (!argc)
136 return Encode(false);
137
138 const Value &v = argv[0];
139 if (!v.isNumber())
140 return Encode(false);
141
142 double dv = v.toNumber();
143 if (std::isnan(x: dv) || qt_is_inf(d: dv))
144 return Encode(false);
145
146 double iv = v.toInteger();
147 return Encode(dv == iv);
148}
149
150ReturnedValue NumberPrototype::method_isSafeInteger(const FunctionObject *, const Value *, const Value *argv, int argc)
151{
152 if (!argc)
153 return Encode(false);
154
155 const Value &v = argv[0];
156 if (!v.isNumber())
157 return Encode(false);
158
159 double dv = v.toNumber();
160 if (std::isnan(x: dv) || qt_is_inf(d: dv))
161 return Encode(false);
162
163 double iv = v.toInteger();
164 return Encode(dv == iv && std::fabs(x: iv) <= (1LL << 53) - 1);
165}
166
167ReturnedValue NumberPrototype::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc)
168{
169 if (!argc || !argv[0].isNumber())
170 return Encode(false);
171
172 double v = argv[0].toNumber();
173 // cast to bool explicitly as std::isnan() may give us ::isnan(), which
174 // sometimes returns an int and we don't want the Encode(int) overload.
175 return Encode(bool(std::isnan(x: v)));
176}
177
178ReturnedValue NumberPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
179{
180 ExecutionEngine *v4 = b->engine();
181 double num = thisNumber(engine: v4, thisObject);
182 if (v4->hasException)
183 return QV4::Encode::undefined();
184
185 if (argc && !argv[0].isUndefined()) {
186 int radix = argv[0].toInt32();
187 if (radix < 2 || radix > 36) {
188 return v4->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix").arg(a: radix));
189 }
190
191 QString str;
192 RuntimeHelpers::numberToString(result: &str, num, radix);
193 return Encode(v4->newString(s: str));
194 }
195
196 return Encode(Value::fromDouble(d: num).toString(e: v4));
197}
198
199ReturnedValue NumberPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
200{
201 Scope scope(b);
202 ScopedValue v(scope, thisNumberValue(v4: b->engine(), thisObject));
203 return Encode(v->toString(e: scope.engine));
204}
205
206ReturnedValue NumberPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int)
207{
208 return thisNumberValue(v4: b->engine(), thisObject);
209}
210
211ReturnedValue NumberPrototype::method_toFixed(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
212{
213 ExecutionEngine *v4 = b->engine();
214 double v = thisNumber(engine: v4, thisObject);
215 if (v4->hasException)
216 return QV4::Encode::undefined();
217
218 double fdigits = 0;
219
220 if (argc > 0)
221 fdigits = argv[0].toInteger();
222
223 if (std::isnan(x: fdigits))
224 fdigits = 0;
225
226 if (fdigits < 0 || fdigits > 100)
227 return v4->throwRangeError(value: *thisObject);
228
229 QString str;
230 if (std::isnan(x: v))
231 str = QStringLiteral("NaN");
232 else if (qt_is_inf(d: v))
233 str = QString::fromLatin1(ba: v < 0 ? "-Infinity" : "Infinity");
234 else if (v < 1.e21)
235 str = NumberLocale::instance()->toString(f: v, format: 'f', precision: int(fdigits));
236 else {
237 return Encode(RuntimeHelpers::stringFromNumber(engine: v4, number: v));
238 }
239 return Encode(v4->newString(s: str));
240}
241
242ReturnedValue NumberPrototype::method_toExponential(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
243{
244 ExecutionEngine *v4 = b->engine();
245 double d = thisNumber(engine: v4, thisObject);
246 if (v4->hasException)
247 return QV4::Encode::undefined();
248
249 bool defaultDigits = !argc || argv[0].isUndefined();
250 int fdigits = !defaultDigits ? argv[0].toInteger() : NumberLocale::instance()->defaultDoublePrecision;
251 if (v4->hasException)
252 return QV4::Encode::undefined();
253
254 if (std::isnan(x: d))
255 return Encode(v4->newString(s: QLatin1String("NaN")));
256
257 if (qIsInf(d))
258 return Encode(v4->newString(s: QLatin1String(d < 0 ? "-Infinity" : "Infinity")));
259
260 if (!defaultDigits && (fdigits < 0 || fdigits > 100)) {
261 Scope scope(v4);
262 ScopedString error(scope, v4->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")));
263 return v4->throwRangeError(value: error);
264 }
265
266 QString result = NumberLocale::instance()->toString(f: d, format: 'e', precision: fdigits);
267 return Encode(v4->newString(s: result));
268}
269
270ReturnedValue NumberPrototype::method_toPrecision(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
271{
272 Scope scope(b);
273 ScopedValue v(scope, thisNumberValue(v4: scope.engine, thisObject));
274 if (scope.hasException())
275 return QV4::Encode::undefined();
276 double d = v->asDouble();
277
278 if (!argc || argv[0].isUndefined())
279 return Encode(v->toString(e: scope.engine));
280
281 int precision = argv[0].toInt32();
282 if (scope.hasException())
283 return QV4::Encode::undefined();
284
285 if (std::isnan(x: d))
286 return Encode(scope.engine->newString(s: QLatin1String("NaN")));
287
288 if (qIsInf(d))
289 return Encode(scope.engine->newString(s: QLatin1String(d < 0 ? "-Infinity" : "Infinity")));
290
291 if (precision < 1 || precision > 100) {
292 ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range")));
293 return scope.engine->throwRangeError(value: error);
294 }
295
296 QString result = NumberLocale::instance()->toString(f: d, format: 'g', precision);
297 return Encode(scope.engine->newString(s: result));
298}
299

source code of qtdeclarative/src/qml/jsruntime/qv4numberobject.cpp