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