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 | |
37 | namespace QV4 { |
38 | struct QObjectSlotDispatcher; |
39 | |
40 | namespace Heap { |
41 | |
42 | struct QQmlValueTypeWrapper; |
43 | |
44 | struct 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 | |
59 | private: |
60 | QV4QPointer<QObject> qObj; |
61 | }; |
62 | |
63 | #define QObjectMethodMembers(class, Member) \ |
64 | Member(class, Pointer, Object *, wrapper) \ |
65 | |
66 | DECLARE_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 | |
100 | struct 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 | |
110 | struct 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 | |
121 | private: |
122 | QV4QPointer<QObject> qObj; |
123 | }; |
124 | |
125 | } |
126 | |
127 | struct 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 | |
202 | protected: |
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 | |
224 | private: |
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 | |
229 | Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags) |
230 | |
231 | inline 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. |
246 | inline 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 | |
254 | inline 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 | |
264 | template <typename ReversalFunctor> |
265 | inline 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 | |
302 | template <typename ReversalFunctor> |
303 | inline 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 | |
348 | struct QQmlValueTypeWrapper; |
349 | |
350 | struct 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> (const QV4::FunctionObject *function); |
375 | }; |
376 | |
377 | |
378 | struct 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 | |
386 | protected: |
387 | static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *); |
388 | static bool virtualIsEqualTo(Managed *a, Managed *b); |
389 | |
390 | private: |
391 | void init(ExecutionEngine *engine); |
392 | ReturnedValue constructInternal(const Value *argv, int argc) const; |
393 | }; |
394 | |
395 | struct 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 | |
407 | using QObjectBiPointer = QBiPointer<QObject, const QObject>; |
408 | |
409 | class MultiplyWrappedQObjectMap : public QObject, |
410 | private QHash<QObjectBiPointer, QV4::WeakValue> |
411 | { |
412 | Q_OBJECT |
413 | public: |
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 | |
460 | private Q_SLOTS: |
461 | void removeDestroyedObject(QObject*); |
462 | }; |
463 | |
464 | } |
465 | |
466 | QT_END_NAMESPACE |
467 | |
468 | #endif // QV4QOBJECTWRAPPER_P_H |
469 | |
470 | |
471 | |