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 auto managed = qv4Value.as<QV4::Managed>();
154 auto engine = managed->engine();
155 auto mm = engine->memoryManager;
156 QV4::Value *m = mm->m_persistentValues->allocate();
157 Q_ASSERT(m);
158 // we create a new strong reference to the heap managed object
159 // to avoid having to rescan the persistent values, we mark it here
160 QV4::WriteBarrier::markCustom(engine, markFunction: [&](QV4::MarkStack *stack){
161 if constexpr (QV4::WriteBarrier::isInsertionBarrier)
162 managed->heapObject()->mark(markStack: stack);
163 });
164 *m = qv4Value;
165 return encodePointer(pointer: m, tag: Kind::QV4ValuePtr);
166 }
167 case QV4::StaticValue::Double_Type:
168 return encode(doubleValue: qv4Value.doubleValue());
169 case QV4::StaticValue::Null_Type:
170 return encodeNull();
171 case QV4::StaticValue::Empty_Type:
172 Q_UNREACHABLE();
173 break;
174 case QV4::StaticValue::Undefined_Type:
175 break;
176 }
177
178 return encodeUndefined();
179 }
180
181 static QString *qStringPtr(quint64 v)
182 {
183 Q_ASSERT(tag(v) == Kind::QStringPtr);
184 return static_cast<QString *>(pointer(raw: v));
185 }
186
187 static quint64 encode(QString stringValue)
188 {
189 return encodePointer(pointer: new QString(std::move(stringValue)), tag: Kind::QStringPtr);
190 }
191
192 static quint64 encode(QLatin1String stringValue)
193 {
194 return encodePointer(pointer: new QString(std::move(stringValue)), tag: Kind::QStringPtr);
195 }
196
197 static QJSValue fromReturnedValue(QV4::ReturnedValue d)
198 {
199 QJSValue result;
200 setValue(jsval: &result, v: d);
201 return result;
202 }
203
204 template<typename T>
205 static const T *asManagedType(const QJSValue *jsval)
206 {
207 if (tag(raw: jsval->d) == Kind::QV4ValuePtr) {
208 if (const QV4::Value *value = qv4ValuePtr(v: jsval->d))
209 return value->as<T>();
210 }
211 return nullptr;
212 }
213
214 // This is a move operation and transfers ownership.
215 static QV4::Value *takeManagedValue(QJSValue *jsval)
216 {
217 if (tag(raw: jsval->d) == Kind::QV4ValuePtr) {
218 if (QV4::Value *value = qv4ValuePtr(v: jsval->d)) {
219 jsval->d = encodeUndefined();
220 return value;
221 }
222 }
223 return nullptr;
224 }
225
226 static QV4::ReturnedValue asPrimitiveType(const QJSValue *jsval)
227 {
228 switch (tag(raw: jsval->d)) {
229 case Kind::BoolValue:
230 return QV4::Encode(boolValue(v: jsval->d));
231 case Kind::IntValue:
232 return QV4::Encode(intValue(v: jsval->d));
233 case Kind::DoublePtr:
234 return QV4::Encode(*doublePtr(v: jsval->d));
235 case Kind::Null:
236 return QV4::Encode::null();
237 case Kind::Undefined:
238 case Kind::QV4ValuePtr:
239 case Kind::QStringPtr:
240 break;
241 }
242
243 return QV4::Encode::undefined();
244 }
245
246 // Beware: This only returns a non-null string if the QJSValue actually holds one.
247 // QV4::Strings are kept as managed values. Retrieve those with getValue().
248 static const QString *asQString(const QJSValue *jsval)
249 {
250 if (tag(raw: jsval->d) == Kind::QStringPtr) {
251 if (const QString *string = qStringPtr(v: jsval->d))
252 return string;
253 }
254 return nullptr;
255 }
256
257 static QV4::ReturnedValue asReturnedValue(const QJSValue *jsval)
258 {
259 switch (tag(raw: jsval->d)) {
260 case Kind::BoolValue:
261 return QV4::Encode(boolValue(v: jsval->d));
262 case Kind::IntValue:
263 return QV4::Encode(intValue(v: jsval->d));
264 case Kind::DoublePtr:
265 return QV4::Encode(*doublePtr(v: jsval->d));
266 case Kind::Null:
267 return QV4::Encode::null();
268 case Kind::QV4ValuePtr:
269 return qv4ValuePtr(v: jsval->d)->asReturnedValue();
270 case Kind::Undefined:
271 case Kind::QStringPtr:
272 break;
273 }
274
275 return QV4::Encode::undefined();
276 }
277
278 static void setString(QJSValue *jsval, QString s)
279 {
280 jsval->d = encode(stringValue: std::move(s));
281 }
282
283 // Only use this with an existing persistent value.
284 // Ownership is transferred to the QJSValue.
285 static void adoptPersistentValue(QJSValue *jsval, QV4::Value *v)
286 {
287 jsval->d = encodePointer(pointer: v, tag: Kind::QV4ValuePtr);
288 }
289
290 static void setValue(QJSValue *jsval, const QV4::Value &v)
291 {
292 jsval->d = encode(qv4Value: v);
293 }
294
295 // Moves any QString onto the V4 heap, changing the value to reflect that.
296 static void manageStringOnV4Heap(QV4::ExecutionEngine *e, QJSValue *jsval)
297 {
298 if (const QString *string = asQString(jsval)) {
299 jsval->d = encode(qv4Value: QV4::Value::fromHeapObject(m: e->newString(s: *string)));
300 delete string;
301 }
302 }
303
304 // Converts any QString on the fly, involving an allocation.
305 // Does not change the value.
306 static QV4::ReturnedValue convertToReturnedValue(QV4::ExecutionEngine *e,
307 const QJSValue &jsval)
308 {
309 if (const QString *string = asQString(jsval: &jsval))
310 return e->newString(s: *string)->asReturnedValue();
311 if (const QV4::Value *val = asManagedType<QV4::Managed>(jsval: &jsval)) {
312 if (QV4::PersistentValueStorage::getEngine(v: val) == e)
313 return val->asReturnedValue();
314
315 qWarning(msg: "JSValue can't be reassigned to another engine.");
316 return QV4::Encode::undefined();
317 }
318 return asPrimitiveType(jsval: &jsval);
319 }
320
321 static QV4::ExecutionEngine *engine(const QJSValue *jsval)
322 {
323 if (tag(raw: jsval->d) == Kind::QV4ValuePtr) {
324 if (const QV4::Value *value = qv4ValuePtr(v: jsval->d))
325 return QV4::PersistentValueStorage::getEngine(v: value);
326 }
327
328 return nullptr;
329 }
330
331 static bool checkEngine(QV4::ExecutionEngine *e, const QJSValue &jsval)
332 {
333 QV4::ExecutionEngine *v4 = engine(jsval: &jsval);
334 return !v4 || v4 == e;
335 }
336
337 static void free(QJSValue *jsval)
338 {
339 switch (tag(raw: jsval->d)) {
340 case Kind::Undefined:
341 case Kind::Null:
342 case Kind::IntValue:
343 case Kind::BoolValue:
344 return;
345 case Kind::DoublePtr:
346 delete doublePtr(v: jsval->d);
347 return;
348 case Kind::QStringPtr:
349 delete qStringPtr(v: jsval->d);
350 return;
351 case Kind::QV4ValuePtr:
352 break;
353 }
354
355 // We need a mutable value for free(). It needs to write to the actual memory.
356 QV4::Value *m = qv4ValuePtr(v: jsval->d);
357 Q_ASSERT(m); // Otherwise it would have been undefined above.
358 if (QV4::ExecutionEngine *e = QV4::PersistentValueStorage::getEngine(v: m)) {
359 if (QJSEngine *jsEngine = e->jsEngine()) {
360 if (jsEngine->thread() != QThread::currentThread()) {
361 QMetaObject::invokeMethod(
362 object: jsEngine, function: [m](){ QV4::PersistentValueStorage::free(v: m); });
363 return;
364 }
365 }
366 }
367 QV4::PersistentValueStorage::free(v: m);
368 }
369};
370
371QT_END_NAMESPACE
372
373#endif
374

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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