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 | #ifndef QQMLOBJECTCREATOR_P_H |
4 | #define QQMLOBJECTCREATOR_P_H |
5 | |
6 | // |
7 | // W A R N I N G |
8 | // ------------- |
9 | // |
10 | // This file is not part of the Qt API. It exists purely as an |
11 | // implementation detail. This header file may change from version to |
12 | // version without notice, or even be removed. |
13 | // |
14 | // We mean it. |
15 | // |
16 | |
17 | #include <private/qqmlimport_p.h> |
18 | #include <private/qqmltypenamecache_p.h> |
19 | #include <private/qv4compileddata_p.h> |
20 | #include <private/qfinitestack_p.h> |
21 | #include <private/qrecursionwatcher_p.h> |
22 | #include <private/qqmlprofiler_p.h> |
23 | #include <private/qv4qmlcontext_p.h> |
24 | #include <private/qqmlguardedcontextdata_p.h> |
25 | #include <private/qqmlfinalizer_p.h> |
26 | #include <private/qqmlvmemetaobject_p.h> |
27 | |
28 | #include <qpointer.h> |
29 | |
30 | QT_BEGIN_NAMESPACE |
31 | |
32 | class QQmlAbstractBinding; |
33 | class QQmlInstantiationInterrupt; |
34 | class QQmlIncubatorPrivate; |
35 | |
36 | struct AliasToRequiredInfo { |
37 | QString propertyName; |
38 | QUrl fileUrl; |
39 | }; |
40 | |
41 | /*! |
42 | \internal |
43 | This struct contains information solely used for displaying error messages |
44 | \variable aliasesToRequired allows us to give the user a way to know which (aliasing) properties |
45 | can be set to set the required property |
46 | \sa QQmlComponentPrivate::unsetRequiredPropertyToQQmlError |
47 | */ |
48 | struct RequiredPropertyInfo |
49 | { |
50 | QString propertyName; |
51 | QUrl fileUrl; |
52 | QV4::CompiledData::Location location; |
53 | QVector<AliasToRequiredInfo> aliasesToRequired; |
54 | }; |
55 | |
56 | struct RequiredPropertyKey |
57 | { |
58 | RequiredPropertyKey() = default; |
59 | RequiredPropertyKey(const QObject *object, const QQmlPropertyData *data) |
60 | : object(object) |
61 | , data(data) |
62 | {} |
63 | |
64 | const QObject *object = nullptr; |
65 | const QQmlPropertyData *data = nullptr; |
66 | |
67 | private: |
68 | friend size_t qHash(const RequiredPropertyKey &key, size_t seed = 0) |
69 | { |
70 | return qHashMulti(seed, args: key.object, args: key.data); |
71 | } |
72 | |
73 | friend bool operator==(const RequiredPropertyKey &a, const RequiredPropertyKey &b) |
74 | { |
75 | return a.object == b.object && a.data == b.data; |
76 | } |
77 | }; |
78 | |
79 | class RequiredProperties : public QHash<RequiredPropertyKey, RequiredPropertyInfo> {}; |
80 | |
81 | struct DeferredQPropertyBinding { |
82 | QObject *target = nullptr; |
83 | int properyIndex = -1; |
84 | QUntypedPropertyBinding binding; |
85 | }; |
86 | |
87 | struct QQmlObjectCreatorSharedState : QQmlRefCounted<QQmlObjectCreatorSharedState> |
88 | { |
89 | QQmlRefPointer<QQmlContextData> rootContext; |
90 | QQmlRefPointer<QQmlContextData> creationContext; |
91 | QFiniteStack<QQmlAbstractBinding::Ptr> allCreatedBindings; |
92 | QFiniteStack<QQmlParserStatus*> allParserStatusCallbacks; |
93 | QFiniteStack<QQmlGuard<QObject> > allCreatedObjects; |
94 | QV4::Value *allJavaScriptObjects; // pointer to vector on JS stack to reference JS wrappers during creation phase. |
95 | QQmlComponentAttached *componentAttached; |
96 | QList<QQmlFinalizerHook *> finalizeHooks; |
97 | QQmlVmeProfiler profiler; |
98 | QRecursionNode recursionNode; |
99 | RequiredProperties requiredProperties; |
100 | QList<DeferredQPropertyBinding> allQPropertyBindings; |
101 | bool hadTopLevelRequiredProperties; |
102 | }; |
103 | |
104 | class Q_QML_PRIVATE_EXPORT QQmlObjectCreator |
105 | { |
106 | Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) |
107 | public: |
108 | QQmlObjectCreator(QQmlRefPointer<QQmlContextData> parentContext, |
109 | const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, |
110 | const QQmlRefPointer<QQmlContextData> &creationContext, |
111 | QQmlIncubatorPrivate *incubator = nullptr); |
112 | ~QQmlObjectCreator(); |
113 | |
114 | enum CreationFlags { NormalObject = 1, InlineComponent = 2 }; |
115 | QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, |
116 | QQmlInstantiationInterrupt *interrupt = nullptr, int flags = NormalObject); |
117 | |
118 | bool populateDeferredProperties(QObject *instance, const QQmlData::DeferredData *deferredData); |
119 | |
120 | void beginPopulateDeferred(const QQmlRefPointer<QQmlContextData> &context); |
121 | void populateDeferredBinding(const QQmlProperty &qmlProperty, int deferredIndex, |
122 | const QV4::CompiledData::Binding *binding); |
123 | void populateDeferredInstance(QObject *outerObject, int deferredIndex, |
124 | int index, QObject *instance, QObject *bindingTarget, |
125 | const QQmlPropertyData *valueTypeProperty, |
126 | const QV4::CompiledData::Binding *binding = nullptr); |
127 | void finalizePopulateDeferred(); |
128 | |
129 | bool finalize(QQmlInstantiationInterrupt &interrupt); |
130 | void clear(); |
131 | |
132 | QQmlRefPointer<QQmlContextData> rootContext() const { return sharedState->rootContext; } |
133 | QQmlComponentAttached **componentAttachment() { return &sharedState->componentAttached; } |
134 | |
135 | QList<QQmlError> errors; |
136 | |
137 | QQmlRefPointer<QQmlContextData> parentContextData() const |
138 | { |
139 | return parentContext.contextData(); |
140 | } |
141 | QFiniteStack<QQmlGuard<QObject> > &allCreatedObjects() { return sharedState->allCreatedObjects; } |
142 | |
143 | RequiredProperties *requiredProperties() {return &sharedState->requiredProperties;} |
144 | bool componentHadTopLevelRequiredProperties() const {return sharedState->hadTopLevelRequiredProperties;} |
145 | |
146 | static QQmlComponent *createComponent(QQmlEngine *engine, |
147 | QV4::ExecutableCompilationUnit *compilationUnit, |
148 | int index, QObject *parent, |
149 | const QQmlRefPointer<QQmlContextData> &context); |
150 | |
151 | void removePendingBinding(QObject *target, int propertyIndex) |
152 | { |
153 | QList<DeferredQPropertyBinding> &pendingBindings = sharedState.data()->allQPropertyBindings; |
154 | auto it = std::remove_if(first: pendingBindings.begin(), last: pendingBindings.end(), |
155 | pred: [&](const DeferredQPropertyBinding &deferred) { |
156 | return deferred.properyIndex == propertyIndex && deferred.target == target; |
157 | }); |
158 | pendingBindings.erase(begin: it, end: pendingBindings.end()); |
159 | } |
160 | |
161 | private: |
162 | QQmlObjectCreator(QQmlRefPointer<QQmlContextData> contextData, |
163 | const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, |
164 | QQmlObjectCreatorSharedState *inheritedSharedState, |
165 | bool isContextObject); |
166 | |
167 | void init(QQmlRefPointer<QQmlContextData> parentContext); |
168 | |
169 | QObject *createInstance(int index, QObject *parent = nullptr, bool isContextObject = false); |
170 | |
171 | bool populateInstance(int index, QObject *instance, QObject *bindingTarget, |
172 | const QQmlPropertyData *valueTypeProperty, |
173 | const QV4::CompiledData::Binding *binding = nullptr); |
174 | |
175 | // If qmlProperty and binding are null, populate all properties, otherwise only the given one. |
176 | void populateDeferred(QObject *instance, int deferredIndex); |
177 | void populateDeferred(QObject *instance, int deferredIndex, |
178 | const QQmlPropertyPrivate *qmlProperty, |
179 | const QV4::CompiledData::Binding *binding); |
180 | |
181 | enum BindingMode { |
182 | ApplyNone = 0x0, |
183 | ApplyImmediate = 0x1, |
184 | ApplyDeferred = 0x2, |
185 | ApplyAll = ApplyImmediate | ApplyDeferred, |
186 | }; |
187 | Q_DECLARE_FLAGS(BindingSetupFlags, BindingMode); |
188 | |
189 | void setupBindings(BindingSetupFlags mode = BindingMode::ApplyImmediate); |
190 | bool setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); |
191 | void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding); |
192 | void setupFunctions(); |
193 | |
194 | QString stringAt(int idx) const { return compilationUnit->stringAt(index: idx); } |
195 | void recordError(const QV4::CompiledData::Location &location, const QString &description); |
196 | |
197 | void registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const; |
198 | |
199 | inline QV4::QmlContext *currentQmlContext(); |
200 | QV4::ResolvedTypeReference *resolvedType(int id) const |
201 | { |
202 | return compilationUnit->resolvedType(id); |
203 | } |
204 | |
205 | enum Phase { |
206 | Startup, |
207 | CreatingObjects, |
208 | CreatingObjectsPhase2, |
209 | ObjectsCreated, |
210 | Finalizing, |
211 | Done |
212 | } phase; |
213 | |
214 | QQmlEngine *engine; |
215 | QV4::ExecutionEngine *v4; |
216 | QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit; |
217 | const QV4::CompiledData::Unit *qmlUnit; |
218 | QQmlGuardedContextData parentContext; |
219 | QQmlRefPointer<QQmlContextData> context; |
220 | const QQmlPropertyCacheVector *propertyCaches; |
221 | QQmlRefPointer<QQmlObjectCreatorSharedState> sharedState; |
222 | bool topLevelCreator; |
223 | bool isContextObject; |
224 | QQmlIncubatorPrivate *incubator; |
225 | |
226 | QObject *_qobject; |
227 | QObject *_scopeObject; |
228 | QObject *_bindingTarget; |
229 | |
230 | const QQmlPropertyData *_valueTypeProperty; // belongs to _qobjectForBindings's property cache |
231 | int _compiledObjectIndex; |
232 | const QV4::CompiledData::Object *_compiledObject; |
233 | QQmlData *_ddata; |
234 | QQmlPropertyCache::ConstPtr _propertyCache; |
235 | QQmlVMEMetaObject *_vmeMetaObject; |
236 | QQmlListProperty<void> _currentList; |
237 | QV4::QmlContext *_qmlContext; |
238 | |
239 | friend struct QQmlObjectCreatorRecursionWatcher; |
240 | |
241 | typedef std::function<bool(QQmlObjectCreatorSharedState *sharedState)> PendingAliasBinding; |
242 | std::vector<PendingAliasBinding> pendingAliasBindings; |
243 | |
244 | template<typename Functor> |
245 | void doPopulateDeferred(QObject *instance, int deferredIndex, Functor f) |
246 | { |
247 | QQmlData *declarativeData = QQmlData::get(object: instance); |
248 | QObject *bindingTarget = instance; |
249 | |
250 | QQmlPropertyCache::ConstPtr cache = declarativeData->propertyCache; |
251 | QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(obj: instance); |
252 | |
253 | QObject *scopeObject = instance; |
254 | qt_ptr_swap(lhs&: _scopeObject, rhs&: scopeObject); |
255 | |
256 | QV4::Scope valueScope(v4); |
257 | QScopedValueRollback<QV4::Value*> jsObjectGuard(sharedState->allJavaScriptObjects, |
258 | valueScope.alloc(nValues: compilationUnit->totalObjectCount())); |
259 | |
260 | Q_ASSERT(topLevelCreator); |
261 | QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc()); |
262 | |
263 | qt_ptr_swap(lhs&: _qmlContext, rhs&: qmlContext); |
264 | |
265 | _propertyCache.swap(other&: cache); |
266 | qt_ptr_swap(lhs&: _qobject, rhs&: instance); |
267 | |
268 | int objectIndex = deferredIndex; |
269 | std::swap(a&: _compiledObjectIndex, b&: objectIndex); |
270 | |
271 | const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index: _compiledObjectIndex); |
272 | qt_ptr_swap(lhs&: _compiledObject, rhs&: obj); |
273 | qt_ptr_swap(lhs&: _ddata, rhs&: declarativeData); |
274 | qt_ptr_swap(lhs&: _bindingTarget, rhs&: bindingTarget); |
275 | qt_ptr_swap(lhs&: _vmeMetaObject, rhs&: vmeMetaObject); |
276 | |
277 | f(); |
278 | |
279 | qt_ptr_swap(lhs&: _vmeMetaObject, rhs&: vmeMetaObject); |
280 | qt_ptr_swap(lhs&: _bindingTarget, rhs&: bindingTarget); |
281 | qt_ptr_swap(lhs&: _ddata, rhs&: declarativeData); |
282 | qt_ptr_swap(lhs&: _compiledObject, rhs&: obj); |
283 | std::swap(a&: _compiledObjectIndex, b&: objectIndex); |
284 | qt_ptr_swap(lhs&: _qobject, rhs&: instance); |
285 | _propertyCache.swap(other&: cache); |
286 | |
287 | qt_ptr_swap(lhs&: _qmlContext, rhs&: qmlContext); |
288 | qt_ptr_swap(lhs&: _scopeObject, rhs&: scopeObject); |
289 | } |
290 | }; |
291 | |
292 | struct QQmlObjectCreatorRecursionWatcher |
293 | { |
294 | QQmlObjectCreatorRecursionWatcher(QQmlObjectCreator *creator); |
295 | |
296 | bool hasRecursed() const { return watcher.hasRecursed(); } |
297 | |
298 | private: |
299 | QQmlRefPointer<QQmlObjectCreatorSharedState> sharedState; |
300 | QRecursionWatcher<QQmlObjectCreatorSharedState, &QQmlObjectCreatorSharedState::recursionNode> watcher; |
301 | }; |
302 | |
303 | QV4::QmlContext *QQmlObjectCreator::currentQmlContext() |
304 | { |
305 | if (!_qmlContext->isManaged()) |
306 | _qmlContext->setM(QV4::QmlContext::create(parent: v4->rootContext(), context, scopeObject: _scopeObject)); |
307 | |
308 | return _qmlContext; |
309 | } |
310 | |
311 | QT_END_NAMESPACE |
312 | |
313 | #endif // QQMLOBJECTCREATOR_P_H |
314 | |