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

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