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
30QT_BEGIN_NAMESPACE
31
32class QQmlAbstractBinding;
33class QQmlInstantiationInterrupt;
34class QQmlIncubatorPrivate;
35
36struct AliasToRequiredInfo {
37 QString propertyName;
38 QUrl fileUrl;
39};
40
41/*!
42\internal
43This 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
45can be set to set the required property
46\sa QQmlComponentPrivate::unsetRequiredPropertyToQQmlError
47*/
48struct RequiredPropertyInfo
49{
50 QString propertyName;
51 QUrl fileUrl;
52 QV4::CompiledData::Location location;
53 QVector<AliasToRequiredInfo> aliasesToRequired;
54};
55
56struct 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
67private:
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
79class RequiredProperties : public QHash<RequiredPropertyKey, RequiredPropertyInfo> {};
80
81struct DeferredQPropertyBinding {
82 QObject *target = nullptr;
83 int properyIndex = -1;
84 QUntypedPropertyBinding binding;
85};
86
87struct 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
104class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
105{
106 Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
107public:
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
161private:
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
292struct QQmlObjectCreatorRecursionWatcher
293{
294 QQmlObjectCreatorRecursionWatcher(QQmlObjectCreator *creator);
295
296 bool hasRecursed() const { return watcher.hasRecursed(); }
297
298private:
299 QQmlRefPointer<QQmlObjectCreatorSharedState> sharedState;
300 QRecursionWatcher<QQmlObjectCreatorSharedState, &QQmlObjectCreatorSharedState::recursionNode> watcher;
301};
302
303QV4::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
311QT_END_NAMESPACE
312
313#endif // QQMLOBJECTCREATOR_P_H
314

source code of qtdeclarative/src/qml/qml/qqmlobjectcreator_p.h