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#ifndef QV4QOBJECTWRAPPER_P_H
5#define QV4QOBJECTWRAPPER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qbipointer_p.h>
19#include <private/qintrusivelist_p.h>
20#include <private/qqmldata_p.h>
21#include <private/qv4functionobject_p.h>
22#include <private/qv4lookup_p.h>
23#include <private/qv4value_p.h>
24
25#include <QtCore/qglobal.h>
26#include <QtCore/qmetatype.h>
27#include <QtCore/qpair.h>
28#include <QtCore/qhash.h>
29
30QT_BEGIN_NAMESPACE
31
32class QObject;
33class QQmlData;
34class QQmlPropertyCache;
35class QQmlPropertyData;
36
37namespace QV4 {
38struct QObjectSlotDispatcher;
39
40namespace Heap {
41
42struct QQmlValueTypeWrapper;
43
44struct Q_QML_EXPORT QObjectWrapper : Object {
45 void init(QObject *object)
46 {
47 Object::init();
48 qObj.init(o: object);
49 }
50
51 void destroy() {
52 qObj.destroy();
53 Object::destroy();
54 }
55
56 QObject *object() const { return qObj.data(); }
57 static void markObjects(Heap::Base *that, MarkStack *markStack);
58
59private:
60 QV4QPointer<QObject> qObj;
61};
62
63#define QObjectMethodMembers(class, Member) \
64 Member(class, Pointer, Object *, wrapper) \
65
66DECLARE_EXPORTED_HEAP_OBJECT(QObjectMethod, FunctionObject) {
67 DECLARE_MARKOBJECTS(QObjectMethod)
68
69 QQmlPropertyData *methods;
70 alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)];
71 int methodCount;
72 int index;
73
74 void init(QV4::ExecutionContext *scope, Object *wrapper, int index);
75 void destroy()
76 {
77 if (methods != reinterpret_cast<const QQmlPropertyData *>(&_singleMethod))
78 delete[] methods;
79 FunctionObject::destroy();
80 }
81
82 void ensureMethodsCache(const QMetaObject *thisMeta);
83 QString name() const;
84
85 const QMetaObject *metaObject() const;
86 QObject *object() const;
87
88 bool isDetached() const;
89 bool isAttachedTo(QObject *o) const;
90
91 enum ThisObjectMode {
92 Invalid,
93 Included,
94 Explicit,
95 };
96
97 QV4::Heap::QObjectMethod::ThisObjectMode checkThisObject(const QMetaObject *thisMeta) const;
98};
99
100struct QMetaObjectWrapper : FunctionObject {
101 const QMetaObject* metaObject;
102 QQmlPropertyData *constructors;
103 int constructorCount;
104
105 void init(const QMetaObject* metaObject);
106 void destroy();
107 void ensureConstructorsCache();
108};
109
110struct QmlSignalHandler : Object {
111 void init(QObject *object, int signalIndex);
112 void destroy() {
113 qObj.destroy();
114 Object::destroy();
115 }
116 int signalIndex;
117
118 QObject *object() const { return qObj.data(); }
119 void setObject(QObject *o) { qObj = o; }
120
121private:
122 QV4QPointer<QObject> qObj;
123};
124
125}
126
127struct Q_QML_EXPORT QObjectWrapper : public Object
128{
129 V4_OBJECT2(QObjectWrapper, Object)
130 V4_NEEDS_DESTROY
131
132 enum Flag {
133 NoFlag = 0x0,
134 CheckRevision = 0x1,
135 AttachMethods = 0x2,
136 AllowOverride = 0x4,
137 IncludeImports = 0x8,
138 };
139
140 Q_DECLARE_FLAGS(Flags, Flag);
141
142 static void initializeBindings(ExecutionEngine *engine);
143
144 const QMetaObject *metaObject() const
145 {
146 if (QObject *o = object())
147 return o->metaObject();
148 return nullptr;
149 }
150
151 QObject *object() const { return d()->object(); }
152
153 ReturnedValue getQmlProperty(
154 const QQmlRefPointer<QQmlContextData> &qmlContext, String *name,
155 Flags flags, bool *hasProperty = nullptr) const;
156
157 static ReturnedValue getQmlProperty(
158 ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
159 Heap::Object *wrapper, QObject *object, String *name, Flags flags,
160 bool *hasProperty = nullptr, const QQmlPropertyData **property = nullptr);
161
162 static bool setQmlProperty(
163 ExecutionEngine *engine, const QQmlRefPointer<QQmlContextData> &qmlContext,
164 QObject *object, String *name, Flags flags, const Value &value);
165
166 static ReturnedValue wrap(ExecutionEngine *engine, QObject *object);
167 static ReturnedValue wrapConst(ExecutionEngine *engine, QObject *object);
168 static void markWrapper(QObject *object, MarkStack *markStack);
169
170 using Object::get;
171
172 static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value);
173 void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value);
174 static void setProperty(
175 ExecutionEngine *engine, QObject *object,
176 const QQmlPropertyData *property, const Value &value);
177
178 void destroyObject(bool lastCall);
179
180 static ReturnedValue getProperty(
181 ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
182 const QQmlPropertyData *property, Flags flags);
183
184 static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
185 static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object);
186
187 template <typename ReversalFunctor> static ReturnedValue lookupPropertyGetterImpl(
188 Lookup *l, ExecutionEngine *engine, const Value &object,
189 Flags flags, ReversalFunctor revert);
190 template <typename ReversalFunctor> static ReturnedValue lookupMethodGetterImpl(
191 Lookup *l, ExecutionEngine *engine, const Value &object,
192 Flags flags, ReversalFunctor revert);
193 static bool virtualResolveLookupSetter(
194 Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
195 static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
196
197 static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a);
198
199 static QString objectToString(
200 ExecutionEngine *engine, const QMetaObject *metaObject, QObject *object);
201
202protected:
203 static bool virtualIsEqualTo(Managed *that, Managed *o);
204 static ReturnedValue create(ExecutionEngine *engine, QObject *object);
205
206 static const QQmlPropertyData *findProperty(
207 QObject *o, const QQmlRefPointer<QQmlContextData> &qmlContext,
208 String *name, Flags flags, QQmlPropertyData *local);
209
210 const QQmlPropertyData *findProperty(
211 const QQmlRefPointer<QQmlContextData> &qmlContext,
212 String *name, Flags flags, QQmlPropertyData *local) const;
213
214 static ReturnedValue virtualGet(
215 const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
216 static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
217 static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
218
219 static ReturnedValue method_connect(
220 const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
221 static ReturnedValue method_disconnect(
222 const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
223
224private:
225 Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
226 Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object);
227};
228
229Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags)
230
231inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object)
232{
233 if (Q_UNLIKELY(QQmlData::wasDeleted(object)))
234 return QV4::Encode::null();
235
236 auto ddata = QQmlData::get(object);
237 if (Q_LIKELY(ddata && ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isUndefined())) {
238 // We own the JS object
239 return ddata->jsWrapper.value();
240 }
241
242 return wrap_slowPath(engine, object);
243}
244
245// Unfortunately we still need a non-const QObject* here because QQmlData needs to register itself in QObjectPrivate.
246inline ReturnedValue QObjectWrapper::wrapConst(ExecutionEngine *engine, QObject *object)
247{
248 if (Q_UNLIKELY(QQmlData::wasDeleted(object)))
249 return QV4::Encode::null();
250
251 return wrapConst_slowPath(engine, object);
252}
253
254inline bool canConvert(const QQmlPropertyCache *fromMo, const QQmlPropertyCache *toMo)
255{
256 while (fromMo) {
257 if (fromMo == toMo)
258 return true;
259 fromMo = fromMo->parent().data();
260 }
261 return false;
262}
263
264template <typename ReversalFunctor>
265inline ReturnedValue QObjectWrapper::lookupPropertyGetterImpl(
266 Lookup *lookup, ExecutionEngine *engine, const Value &object,
267 QObjectWrapper::Flags flags, ReversalFunctor revertLookup)
268{
269 // we can safely cast to a QV4::Object here. If object is something else,
270 // the internal class won't match
271 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
272 if (!o || o->internalClass != lookup->qobjectLookup.ic)
273 return revertLookup();
274
275 Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper *>(o);
276 QObject *qobj = This->object();
277 if (QQmlData::wasDeleted(object: qobj))
278 return QV4::Encode::undefined();
279
280 QQmlData *ddata = QQmlData::get(object: qobj, /*create*/create: false);
281 if (!ddata)
282 return revertLookup();
283
284 const QQmlPropertyData *property = lookup->qobjectLookup.propertyData;
285 if (ddata->propertyCache.data() != lookup->qobjectLookup.propertyCache) {
286 // If the property is overridden and the lookup allows overrides to be considered,
287 // we have to revert here and redo the lookup from scratch.
288 if (property->isOverridden()
289 && ((flags & AllowOverride)
290 || property->isFunction()
291 || property->isSignalHandler())) {
292 return revertLookup();
293 }
294
295 if (!canConvert(fromMo: ddata->propertyCache.data(), toMo: lookup->qobjectLookup.propertyCache))
296 return revertLookup();
297 }
298
299 return getProperty(engine, wrapper: This, object: qobj, property, flags);
300}
301
302template <typename ReversalFunctor>
303inline ReturnedValue QObjectWrapper::lookupMethodGetterImpl(
304 Lookup *lookup, ExecutionEngine *engine, const Value &object,
305 QObjectWrapper::Flags flags, ReversalFunctor revertLookup)
306{
307 // we can safely cast to a QV4::Object here. If object is something else,
308 // the internal class won't match
309 Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
310 if (!o || o->internalClass != lookup->qobjectMethodLookup.ic)
311 return revertLookup();
312
313 Heap::QObjectWrapper *This = static_cast<Heap::QObjectWrapper *>(o);
314 QObject *qobj = This->object();
315 if (QQmlData::wasDeleted(object: qobj))
316 return QV4::Encode::undefined();
317
318 QQmlData *ddata = QQmlData::get(object: qobj, /*create*/create: false);
319 if (!ddata)
320 return revertLookup();
321
322 const QQmlPropertyData *property = lookup->qobjectMethodLookup.propertyData;
323 if (ddata->propertyCache.data() != lookup->qobjectMethodLookup.propertyCache) {
324 if (property && property->isOverridden())
325 return revertLookup();
326
327 if (!canConvert(fromMo: ddata->propertyCache.data(), toMo: lookup->qobjectMethodLookup.propertyCache))
328 return revertLookup();
329 }
330
331 if (Heap::QObjectMethod *method = lookup->qobjectMethodLookup.method) {
332 if (method->isDetached())
333 return method->asReturnedValue();
334 }
335
336 if (!property) // was toString() or destroy()
337 return revertLookup();
338
339 QV4::Scope scope(engine);
340 QV4::ScopedValue v(scope, getProperty(engine, wrapper: This, object: qobj, property, flags));
341 if (!v->as<QObjectMethod>())
342 return revertLookup();
343
344 lookup->qobjectMethodLookup.method = static_cast<Heap::QObjectMethod *>(v->heapObject());
345 return v->asReturnedValue();
346}
347
348struct QQmlValueTypeWrapper;
349
350struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
351{
352 V4_OBJECT2(QObjectMethod, QV4::FunctionObject)
353 V4_NEEDS_DESTROY
354
355 enum { DestroyMethod = -1, ToStringMethod = -2 };
356
357 static ReturnedValue create(QV4::ExecutionContext *scope, Heap::Object *wrapper, int index);
358 static ReturnedValue create(
359 QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
360 static ReturnedValue create(QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
361 Heap::Object *wrapper, Heap::Object *object);
362
363 int methodIndex() const { return d()->index; }
364 QObject *object() const { return d()->object(); }
365
366 QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine, QObject *o) const;
367 QV4::ReturnedValue method_destroy(
368 QV4::ExecutionEngine *ctx, QObject *o, const Value *args, int argc) const;
369
370 static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
371
372 ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const;
373
374 static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function);
375};
376
377
378struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject
379{
380 V4_OBJECT2(QMetaObjectWrapper, QV4::FunctionObject)
381 V4_NEEDS_DESTROY
382
383 static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
384 const QMetaObject *metaObject() const { return d()->metaObject; }
385
386protected:
387 static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
388 static bool virtualIsEqualTo(Managed *a, Managed *b);
389
390private:
391 void init(ExecutionEngine *engine);
392 ReturnedValue constructInternal(const Value *argv, int argc) const;
393};
394
395struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object
396{
397 V4_OBJECT2(QmlSignalHandler, QV4::Object)
398 V4_PROTOTYPE(signalHandlerPrototype)
399 V4_NEEDS_DESTROY
400
401 int signalIndex() const { return d()->signalIndex; }
402 QObject *object() const { return d()->object(); }
403
404 static void initProto(ExecutionEngine *v4);
405};
406
407using QObjectBiPointer = QBiPointer<QObject, const QObject>;
408
409class MultiplyWrappedQObjectMap : public QObject,
410 private QHash<QObjectBiPointer, QV4::WeakValue>
411{
412 Q_OBJECT
413public:
414 typedef QHash<QObjectBiPointer, QV4::WeakValue>::ConstIterator ConstIterator;
415 typedef QHash<QObjectBiPointer, QV4::WeakValue>::Iterator Iterator;
416
417 using value_type = QHash<QObjectBiPointer, QV4::WeakValue>::value_type;
418
419 ConstIterator begin() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constBegin(); }
420 Iterator begin() { return QHash<QObjectBiPointer, QV4::WeakValue>::begin(); }
421 ConstIterator end() const { return QHash<QObjectBiPointer, QV4::WeakValue>::constEnd(); }
422 Iterator end() { return QHash<QObjectBiPointer, QV4::WeakValue>::end(); }
423
424 template<typename Pointer>
425 void insert(Pointer key, Heap::Object *value)
426 {
427 QHash<QObjectBiPointer, WeakValue>::operator[](key).set(value->internalClass->engine, value);
428 connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
429 }
430
431 template<typename Pointer>
432 ReturnedValue value(Pointer key) const
433 {
434 ConstIterator it = find(key);
435 return it == end()
436 ? QV4::WeakValue().value()
437 : it->value();
438 }
439
440 Iterator erase(Iterator it);
441
442 template<typename Pointer>
443 void remove(Pointer key)
444 {
445 Iterator it = find(key);
446 if (it == end())
447 return;
448 erase(it);
449 }
450
451 template<typename Pointer>
452 void mark(Pointer key, MarkStack *markStack)
453 {
454 Iterator it = find(key);
455 if (it == end())
456 return;
457 it->markOnce(markStack);
458 }
459
460private Q_SLOTS:
461 void removeDestroyedObject(QObject*);
462};
463
464}
465
466QT_END_NAMESPACE
467
468#endif // QV4QOBJECTWRAPPER_P_H
469
470
471

source code of qtdeclarative/src/qml/jsruntime/qv4qobjectwrapper_p.h