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//
5// W A R N I N G
6// -------------
7//
8// This file is not part of the Qt API. It exists purely as an
9// implementation detail. This header file may change from version to
10// version without notice, or even be removed.
11//
12// We mean it.
13//
14
15#ifndef QJSVALUE_P_H
16#define QJSVALUE_P_H
17
18#include <qjsvalue.h>
19#include <private/qtqmlglobal_p.h>
20#include <private/qv4value_p.h>
21#include <private/qv4string_p.h>
22#include <private/qv4engine_p.h>
23#include <private/qv4mm_p.h>
24#include <private/qv4persistent_p.h>
25
26#include <QtCore/qthread.h>
27
28QT_BEGIN_NAMESPACE
29
30class QJSValuePrivate
31{
32 static constexpr quint64 s_tagBits = 3; // 3 bits mask
33 static constexpr quint64 s_tagMask = (1 << s_tagBits) - 1;
34
35 static constexpr quint64 s_pointerBit = 0x1;
36
37public:
38 enum class Kind {
39 Undefined = 0x0,
40 Null = 0x2,
41 IntValue = 0x4,
42 BoolValue = 0x6,
43 DoublePtr = 0x0 | s_pointerBit,
44 QV4ValuePtr = 0x2 | s_pointerBit,
45 QStringPtr = 0x4 | s_pointerBit,
46 };
47
48 static_assert(quint64(Kind::Undefined) <= s_tagMask);
49 static_assert(quint64(Kind::Null) <= s_tagMask);
50 static_assert(quint64(Kind::IntValue) <= s_tagMask);
51 static_assert(quint64(Kind::BoolValue) <= s_tagMask);
52 static_assert(quint64(Kind::DoublePtr) <= s_tagMask);
53 static_assert(quint64(Kind::QV4ValuePtr) <= s_tagMask);
54 static_assert(quint64(Kind::QStringPtr) <= s_tagMask);
55
56 static Kind tag(quint64 raw) { return Kind(raw & s_tagMask); }
57
58#if QT_POINTER_SIZE == 4
59 static void *pointer(quint64 raw)
60 {
61 Q_ASSERT(quint64(tag(raw)) & s_pointerBit);
62 return reinterpret_cast<void *>(raw >> 32);
63 }
64
65 static quint64 encodePointer(void *pointer, Kind tag)
66 {
67 Q_ASSERT(quint64(tag) & s_pointerBit);
68 return (quint64(quintptr(pointer)) << 32) | quint64(tag);
69 }
70#else
71 static constexpr quint64 s_minAlignment = 1 << s_tagBits;
72 static_assert(alignof(double) >= s_minAlignment);
73 static_assert(alignof(QV4::Value) >= s_minAlignment);
74 static_assert(alignof(QString) >= s_minAlignment);
75
76 static void *pointer(quint64 raw)
77 {
78 Q_ASSERT(quint64(tag(raw)) & s_pointerBit);
79 return reinterpret_cast<void *>(raw & ~s_tagMask);
80 }
81
82 static quint64 encodePointer(void *pointer, Kind tag)
83 {
84 Q_ASSERT(quint64(tag) & s_pointerBit);
85 return quintptr(pointer) | quint64(tag);
86 }
87#endif
88
89 static quint64 encodeUndefined()
90 {
91 return quint64(Kind::Undefined);
92 }
93
94 static quint64 encodeNull()
95 {
96 return quint64(Kind::Null);
97 }
98
99 static int intValue(quint64 v)
100 {
101 Q_ASSERT(tag(v) == Kind::IntValue);
102 return v >> 32;
103 }
104
105 static quint64 encode(int intValue)
106 {
107 return (quint64(intValue) << 32) | quint64(Kind::IntValue);
108 }
109
110 static quint64 encode(uint uintValue)
111 {
112 return (uintValue < uint(std::numeric_limits<int>::max()))
113 ? encode(intValue: int(uintValue))
114 : encode(doubleValue: double(uintValue));
115 }
116
117 static bool boolValue(quint64 v)
118 {
119 Q_ASSERT(tag(v) == Kind::BoolValue);
120 return v >> 32;
121 }
122
123 static quint64 encode(bool boolValue)
124 {
125 return (quint64(boolValue) << 32) | quint64(Kind::BoolValue);
126 }
127
128 static double *doublePtr(quint64 v)
129 {
130 Q_ASSERT(tag(v) == Kind::DoublePtr);
131 return static_cast<double *>(pointer(raw: v));
132 }
133
134 static quint64 encode(double doubleValue)
135 {
136 return encodePointer(pointer: new double(doubleValue), tag: Kind::DoublePtr);
137 }
138
139 static QV4::Value *qv4ValuePtr(quint64 v)
140 {
141 Q_ASSERT(tag(v) == Kind::QV4ValuePtr);
142 return static_cast<QV4::Value *>(pointer(raw: v));
143 }
144
145 static quint64 encode(const QV4::Value &qv4Value)
146 {
147 switch (qv4Value.type()) {
148 case QV4::StaticValue::Boolean_Type:
149 return encode(boolValue: qv4Value.booleanValue());
150 case QV4::StaticValue::Integer_Type:
151 return encode(intValue: qv4Value.integerValue());
152 case QV4::StaticValue::Managed_Type: {
153 QV4::Value *m = qv4Value.as<QV4::Managed>()->engine()
154 ->memoryManager->m_persistentValues->allocate();
155 Q_ASSERT(m);
156 *m = qv4Value;
157 return encodePointer(pointer: m, tag: Kind::QV4ValuePtr);
158 }
159 case QV4::StaticValue::Double_Type:
160 return encode(doubleValue: qv4Value.doubleValue());
161 case QV4::StaticValue::Null_Type:
162 return encodeNull();
163 case QV4::StaticValue::Empty_Type:
164 Q_UNREACHABLE();
165 break;
166 case QV4::StaticValue::Undefined_Type:
167 break;
168 }
169
170 return encodeUndefined();
171 }
172
173 static QString *qStringPtr(quint64 v)
174 {
175 Q_ASSERT(tag(v) == Kind::QStringPtr);
176 return static_cast<QString *>(pointer(raw: v));
177 }
178
179 static quint64 encode(QString stringValue)
180 {
181 return encodePointer(pointer: new QString(std::move(stringValue)), tag: Kind::QStringPtr);
182 }
183
184 static quint64 encode(QLatin1String stringValue)
185 {
186 return encodePointer(pointer: new QString(std::move(stringValue)), tag: Kind::QStringPtr);
187 }
188
189 static QJSValue fromReturnedValue(QV4::ReturnedValue d)
190 {
191 QJSValue result;
192 setValue(jsval: &result, v: d);
193 return result;
194 }
195
196 template<typename T>
197 static const T *asManagedType(const QJSValue *jsval)
198 {
199 if (tag(raw: jsval->d) == Kind::QV4ValuePtr) {
200 if (const QV4::Value *value = qv4ValuePtr(v: jsval->d))
201 return value->as<T>();
202 }
203 return nullptr;
204 }
205
206 // This is a move operation and transfers ownership.
207 static QV4::Value *takeManagedValue(QJSValue *jsval)
208 {
209 if (tag(raw: jsval->d) == Kind::QV4ValuePtr) {
210 if (QV4::Value *value = qv4ValuePtr(v: jsval->d)) {
211 jsval->d = encodeUndefined();
212 return value;
213 }
214 }
215 return nullptr;
216 }
217
218 static QV4::ReturnedValue asPrimitiveType(const QJSValue *jsval)
219 {
220 switch (tag(raw: jsval->d)) {
221 case Kind::BoolValue:
222 return QV4::Encode(boolValue(v: jsval->d));
223 case Kind::IntValue:
224 return QV4::Encode(intValue(v: jsval->d));
225 case Kind::DoublePtr:
226 return QV4::Encode(*doublePtr(v: jsval->d));
227 case Kind::Null:
228 return QV4::Encode::null();
229 case Kind::Undefined:
230 case Kind::QV4ValuePtr:
231 case Kind::QStringPtr:
232 break;
233 }
234
235 return QV4::Encode::undefined();
236 }
237
238 // Beware: This only returns a non-null string if the QJSValue actually holds one.
239 // QV4::Strings are kept as managed values. Retrieve those with getValue().
240 static const QString *asQString(const QJSValue *jsval)
241 {
242 if (tag(raw: jsval->d) == Kind::QStringPtr) {
243 if (const QString *string = qStringPtr(v: jsval->d))
244 return string;
245 }
246 return nullptr;
247 }
248
249 static QV4::ReturnedValue asReturnedValue(const QJSValue *jsval)
250 {
251 switch (tag(raw: jsval->d)) {
252 case Kind::BoolValue:
253 return QV4::Encode(boolValue(v: jsval->d));
254 case Kind::IntValue:
255 return QV4::Encode(intValue(v: jsval->d));
256 case Kind::DoublePtr:
257 return QV4::Encode(*doublePtr(v: jsval->d));
258 case Kind::Null:
259 return QV4::Encode::null();
260 case Kind::QV4ValuePtr:
261 return qv4ValuePtr(v: jsval->d)->asReturnedValue();
262 case Kind::Undefined:
263 case Kind::QStringPtr:
264 break;
265 }
266
267 return QV4::Encode::undefined();
268 }
269
270 static void setString(QJSValue *jsval, QString s)
271 {
272 jsval->d = encode(stringValue: std::move(s));
273 }
274
275 // Only use this with an existing persistent value.
276 // Ownership is transferred to the QJSValue.
277 static void adoptPersistentValue(QJSValue *jsval, QV4::Value *v)
278 {
279 jsval->d = encodePointer(pointer: v, tag: Kind::QV4ValuePtr);
280 }
281
282 static void setValue(QJSValue *jsval, const QV4::Value &v)
283 {
284 jsval->d = encode(qv4Value: v);
285 }
286
287 // Moves any QString onto the V4 heap, changing the value to reflect that.
288 static void manageStringOnV4Heap(QV4::ExecutionEngine *e, QJSValue *jsval)
289 {
290 if (const QString *string = asQString(jsval)) {
291 jsval->d = encode(qv4Value: QV4::Value::fromHeapObject(m: e->newString(s: *string)));
292 delete string;
293 }
294 }
295
296 // Converts any QString on the fly, involving an allocation.
297 // Does not change the value.
298 static QV4::ReturnedValue convertToReturnedValue(QV4::ExecutionEngine *e,
299 const QJSValue &jsval)
300 {
301 if (const QString *string = asQString(jsval: &jsval))
302 return e->newString(s: *string)->asReturnedValue();
303 if (const QV4::Value *val = asManagedType<QV4::Managed>(jsval: &jsval)) {
304 if (QV4::PersistentValueStorage::getEngine(v: val) == e)
305 return val->asReturnedValue();
306
307 qWarning(msg: "JSValue can't be reassigned to another engine.");
308 return QV4::Encode::undefined();
309 }
310 return asPrimitiveType(jsval: &jsval);
311 }
312
313 static QV4::ExecutionEngine *engine(const QJSValue *jsval)
314 {
315 if (tag(raw: jsval->d) == Kind::QV4ValuePtr) {
316 if (const QV4::Value *value = qv4ValuePtr(v: jsval->d))
317 return QV4::PersistentValueStorage::getEngine(v: value);
318 }
319
320 return nullptr;
321 }
322
323 static bool checkEngine(QV4::ExecutionEngine *e, const QJSValue &jsval)
324 {
325 QV4::ExecutionEngine *v4 = engine(jsval: &jsval);
326 return !v4 || v4 == e;
327 }
328
329 static void free(QJSValue *jsval)
330 {
331 switch (tag(raw: jsval->d)) {
332 case Kind::Undefined:
333 case Kind::Null:
334 case Kind::IntValue:
335 case Kind::BoolValue:
336 return;
337 case Kind::DoublePtr:
338 delete doublePtr(v: jsval->d);
339 return;
340 case Kind::QStringPtr:
341 delete qStringPtr(v: jsval->d);
342 return;
343 case Kind::QV4ValuePtr:
344 break;
345 }
346
347 // We need a mutable value for free(). It needs to write to the actual memory.
348 QV4::Value *m = qv4ValuePtr(v: jsval->d);
349 Q_ASSERT(m); // Otherwise it would have been undefined above.
350 if (QV4::ExecutionEngine *e = QV4::PersistentValueStorage::getEngine(v: m)) {
351 if (QJSEngine *jsEngine = e->jsEngine()) {
352 if (jsEngine->thread() != QThread::currentThread()) {
353 QMetaObject::invokeMethod(
354 object: jsEngine, function: [m](){ QV4::PersistentValueStorage::free(v: m); });
355 return;
356 }
357 }
358 }
359 QV4::PersistentValueStorage::free(v: m);
360 }
361};
362
363QT_END_NAMESPACE
364
365#endif
366

source code of qtdeclarative/src/qml/jsapi/qjsvalue_p.h