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 <qv4runtime_p.h>
5#include <qv4propertykey_p.h>
6#include <qv4string_p.h>
7#include <qv4symbol_p.h>
8#include <qv4object_p.h>
9#include <qv4objectproto_p.h>
10#include <private/qv4mm_p.h>
11
12#include <wtf/MathExtras.h>
13
14using namespace QV4;
15
16int Value::toUInt16() const
17{
18 if (integerCompatible())
19 return (ushort)(uint)integerValue();
20
21 double number = toNumber();
22
23 double D16 = 65536.0;
24 if ((number >= 0 && number < D16))
25 return static_cast<ushort>(number);
26
27 if (!std::isfinite(x: number))
28 return +0;
29
30 double d = ::floor(x: ::fabs(x: number));
31 if (std::signbit(x: number))
32 d = -d;
33
34 number = ::fmod(x: d , y: D16);
35
36 if (number < 0)
37 number += D16;
38
39 return (unsigned short)number;
40}
41
42bool Value::toBooleanImpl(Value val)
43{
44 if (val.isManagedOrUndefined()) {
45 Heap::Base *b = val.m();
46 if (!b)
47 return false;
48 if (b->internalClass->vtable->isString)
49 return static_cast<Heap::String *>(b)->length() > 0;
50 return true;
51 }
52
53 // double
54 double d = val.doubleValue();
55 return d && !std::isnan(x: d);
56}
57
58double Value::toNumberImpl(Value val)
59{
60 switch (val.type()) {
61 case QV4::Value::Undefined_Type:
62 return std::numeric_limits<double>::quiet_NaN();
63 case QV4::Value::Managed_Type:
64 if (String *s = val.stringValue())
65 return RuntimeHelpers::stringToNumber(s: s->toQString());
66 if (val.isSymbol()) {
67 Managed &m = static_cast<Managed &>(val);
68 m.engine()->throwTypeError();
69 return 0;
70 }
71 {
72 Q_ASSERT(val.isObject());
73 Scope scope(val.objectValue()->engine());
74 ScopedValue protectThis(scope, val);
75 ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value: val, typeHint: NUMBER_HINT));
76 if (scope.hasException())
77 return 0;
78 return prim->toNumber();
79 }
80 case QV4::Value::Null_Type:
81 case QV4::Value::Boolean_Type:
82 case QV4::Value::Integer_Type:
83 return val.int_32();
84 default: // double
85 Q_UNREACHABLE();
86 }
87}
88
89static QString primitiveToQString(const Value *value)
90{
91 switch (value->type()) {
92 case Value::Empty_Type:
93 Q_ASSERT(!"empty Value encountered");
94 Q_UNREACHABLE_RETURN(QString());
95 case Value::Undefined_Type:
96 return QStringLiteral("undefined");
97 case Value::Null_Type:
98 return QStringLiteral("null");
99 case Value::Boolean_Type:
100 if (value->booleanValue())
101 return QStringLiteral("true");
102 else
103 return QStringLiteral("false");
104 case Value::Managed_Type:
105 Q_UNREACHABLE_RETURN(QString());
106 case Value::Integer_Type: {
107 QString str;
108 RuntimeHelpers::numberToString(result: &str, num: (double)value->int_32(), radix: 10);
109 return str;
110 }
111 case Value::Double_Type: {
112 QString str;
113 RuntimeHelpers::numberToString(result: &str, num: value->doubleValue(), radix: 10);
114 return str;
115 }
116 } // switch
117
118 Q_UNREACHABLE_RETURN(QString());
119}
120
121
122QString Value::toQStringNoThrow() const
123{
124 if (isManaged()) {
125 if (String *s = stringValue())
126 return s->toQString();
127 if (Symbol *s = symbolValue())
128 return s->descriptiveString();
129
130 Q_ASSERT(isObject());
131 Scope scope(objectValue()->engine());
132 ScopedValue ex(scope);
133 bool caughtException = false;
134 ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value: *this, typeHint: STRING_HINT));
135 if (scope.hasException()) {
136 ex = scope.engine->catchException();
137 caughtException = true;
138 } else if (prim->isPrimitive()) {
139 return prim->toQStringNoThrow();
140 }
141
142 // Can't nest try/catch due to CXX ABI limitations for foreign exception nesting.
143 if (caughtException) {
144 ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value: ex, typeHint: STRING_HINT));
145 if (scope.hasException()) {
146 ex = scope.engine->catchException();
147 } else if (prim->isPrimitive()) {
148 return prim->toQStringNoThrow();
149 }
150 }
151
152 return QString();
153 }
154
155 return primitiveToQString(value: this);
156}
157
158QString Value::toQString() const
159{
160 if (isManaged()) {
161 if (String *s = stringValue())
162 return s->toQString();
163
164 if (isSymbol()) {
165 static_cast<const Managed *>(this)->engine()->throwTypeError();
166 return QString();
167 }
168
169 Q_ASSERT(isObject());
170 Scope scope(objectValue()->engine());
171 ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value: *this, typeHint: STRING_HINT));
172 return prim->toQString();
173 }
174
175 return primitiveToQString(value: this);
176}
177
178QString Value::toQString(bool *ok) const
179{
180 if (isManaged()) {
181 if (String *s = stringValue()) {
182 *ok = true;
183 return s->toQString();
184 }
185
186 if (isSymbol()) {
187 static_cast<const Managed *>(this)->engine()->throwTypeError();
188 *ok = false;
189 return QString();
190 }
191
192 Q_ASSERT(isObject());
193 Scope scope(objectValue()->engine());
194 ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value: *this, typeHint: STRING_HINT));
195
196 if (scope.hasException()) {
197 *ok = false;
198 return QString();
199 }
200
201 return prim->toQString(ok);
202 }
203
204 return primitiveToQString(value: this);
205}
206
207QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const
208{
209 if (isInteger() && int_32() >= 0)
210 return PropertyKey::fromArrayIndex(idx: static_cast<uint>(int_32()));
211 if (isStringOrSymbol()) {
212 Scope scope(e);
213 ScopedStringOrSymbol s(scope, this);
214 return s->toPropertyKey();
215 }
216 Scope scope(e);
217 ScopedValue v(scope, RuntimeHelpers::toPrimitive(value: *this, typeHint: STRING_HINT));
218 if (!v->isStringOrSymbol())
219 v = v->toString(e);
220 if (e->hasException)
221 return PropertyKey::invalid();
222 ScopedStringOrSymbol s(scope, v);
223 return s->toPropertyKey();
224}
225
226bool Value::sameValue(Value other) const {
227 if (_val == other._val)
228 return true;
229 String *s = stringValue();
230 String *os = other.stringValue();
231 if (s && os)
232 return s->isEqualTo(other: os);
233 if (isInteger() && other.isDouble())
234 return int_32() ? (double(int_32()) == other.doubleValue())
235 : (other.doubleValue() == 0 && !std::signbit(x: other.doubleValue()));
236 if (isDouble() && other.isInteger())
237 return other.int_32() ? (doubleValue() == double(other.int_32()))
238 : (doubleValue() == 0 && !std::signbit(x: doubleValue()));
239 if (isManaged())
240 return other.isManaged() && cast<Managed>()->isEqualTo(other: other.cast<Managed>());
241 return false;
242}
243
244bool Value::sameValueZero(Value other) const {
245 if (_val == other._val)
246 return true;
247 String *s = stringValue();
248 String *os = other.stringValue();
249 if (s && os)
250 return s->isEqualTo(other: os);
251 if (isInteger() && other.isDouble())
252 return double(int_32()) == other.doubleValue();
253 if (isDouble() && other.isInteger())
254 return other.int_32() == doubleValue();
255 if (isDouble() && other.isDouble()) {
256 if (doubleValue() == 0 && other.doubleValue() == 0) {
257 return true;
258 }
259 }
260 if (isManaged())
261 return other.isManaged() && cast<Managed>()->isEqualTo(other: other.cast<Managed>());
262 return false;
263}
264
265Heap::String *Value::toString(ExecutionEngine *e, Value val)
266{
267 return RuntimeHelpers::convertToString(engine: e, value: val);
268}
269
270Heap::Object *Value::toObject(ExecutionEngine *e, Value val)
271{
272 return RuntimeHelpers::convertToObject(engine: e, value: val);
273}
274
275uint Value::asArrayLength(bool *ok) const
276{
277 *ok = true;
278 if (isInteger()) {
279 if (int_32() >= 0) {
280 return (uint)int_32();
281 } else {
282 *ok = false;
283 return UINT_MAX;
284 }
285 }
286 if (isNumber()) {
287 double d = doubleValue();
288 uint idx = (uint)d;
289 if (idx != d) {
290 *ok = false;
291 return UINT_MAX;
292 }
293 return idx;
294 }
295 if (String *s = stringValue())
296 return s->toUInt(ok);
297
298 uint idx = toUInt32();
299 double d = toNumber();
300 if (d != idx) {
301 *ok = false;
302 return UINT_MAX;
303 }
304 return idx;
305}
306

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