Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlpreviewobjectpatch.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 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// Qt-Security score:significant reason:default
4
6
7#include <private/qqmlcomponent_p.h>
8#include <private/qqmlcontextdata_p.h>
9#include <private/qqmldata_p.h>
10#include <private/qqmljavascriptexpression_p.h>
11#include <private/qqmlobjectcreator_p.h>
12#include <private/qqmlpreviewbindingpatchcontext_p.h>
13#include <private/qqmlpreviewdiff_p.h>
14#include <private/qqmlpropertyresolver_p.h>
15#include <private/qqmlscriptdata_p.h>
16#include <private/qqmlsignalnames_p.h>
17#include <private/qqmlvme_p.h>
18#include <private/qqmlvmemetaobject_p.h>
19#include <private/qv4functionobject_p.h>
20#include <private/qv4generatorobject_p.h>
21#include <private/qv4qmlcontext_p.h>
22#include <private/qv4resolvedtypereference_p.h>
23
24#include <QtQml/qqmlcomponent.h>
25#include <QtQml/qqmlproperty.h>
26
27#include <QtCore/qset.h>
28
30
31namespace QQmlPreview {
32
33// The indices at which object appears in oldUnit: its own (ddata) index plus any indices it
34// occupies through composite base levels (the VME meta-object chain).
35static QVarLengthArray<int, 4>
36objectIndices(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit)
37{
38 QVarLengthArray<int, 4> objectIndices;
39
40 QQmlData *ddata = QQmlData::get(object);
41 if (!ddata)
42 return objectIndices;
43
44 if (ddata->compilationUnit == oldUnit)
45 objectIndices.push_back(ddata->cuObjectIndex);
46 if (ddata->hasVMEMetaObject) {
47 for (QQmlVMEMetaObject *vme =
48 static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(object)->metaObject);
49 vme; vme = vme->parentVMEMetaObject()) {
50 if (vme->compilationUnit() == oldUnit)
51 objectIndices.push_back(vme->qmlObjectId());
52 }
53 }
54
55 return objectIndices;
56}
57
59nonCompositeBaseType(const QQmlPropertyCache::ConstPtr &propertyCache)
60{
61 for (QQmlPropertyCache::ConstPtr parent = propertyCache; parent; parent = parent->parent()) {
62 if (!parent->isComposite())
63 return parent;
64 }
65
66 return QQmlPropertyCache::ConstPtr();
67}
68
69static bool
70hasChangedNonCompositeBaseType(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
71 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit,
72 int objectIndex)
73{
74 const QV4::CompiledData::Object *oldObj = oldUnit->objectAt(objectIndex);
75 const auto *oldTypeRef = oldUnit->resolvedType(oldObj->inheritedTypeNameIndex);
76 if (!oldTypeRef)
77 return false; // Grouped property sub-objects have no inherited type.
78
79 const QV4::CompiledData::Object *newObj = newUnit->objectAt(objectIndex);
80 const auto *newTypeRef = newUnit->resolvedType(newObj->inheritedTypeNameIndex);
81 if (!newTypeRef)
82 return true; // Type disappeared — definitely changed.
83
84 return nonCompositeBaseType(oldTypeRef->typePropertyCache())
85 != nonCompositeBaseType(newTypeRef->typePropertyCache());
86}
87
88// Whether the object at objectIndex is the root of a component: The document, an inline component,
89// or an explicit or implicit Component.
90static bool isComponentRoot(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
91 int objectIndex)
92{
93 if (objectIndex == 0)
94 return true;
95
96 if (objectIndex >= unit->objectCount())
97 return false;
98
99 if (unit->objectAt(objectIndex)->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot))
100 return true;
101
102 if (unit->baseCompilationUnit()->implicitComponentForObject(objectIndex) >= 0)
103 return true;
104
105 for (int i = 0, end = unit->objectCount(); i < end; ++i) {
106 if (unit->objectIsComponent(i)
107 && int(unit->objectAt(i)->bindingTable()->value.objectIndex) == objectIndex) {
108 return true;
109 }
110 }
111 return false;
112}
113
114// reset() + repopulateBindings() cannot faithfully reproduce some components in place,
115// because either:
116// - Its index doesn't denote a component root anymore in the new unit, because objects were
117// added or removed in front of it.
118// - Its non-composite (C++) base type changed: the reused QObject is still an instance of the old
119// class (see hasChangedNonCompositeBaseType).
120// - It carries deferred bindings. We cannot re-install those because their handling is in the
121// type's discretion.
122// Fore those return false here. Otherwise return true.
123static bool
124canRebuildComponentRootInPlace(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
125 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit,
126 int objectIndex)
127{
128 // An index out of range in the new CU is obsolete: rebuildObject() skips it and applyDiff()
129 // retires it afterwards. There is nothing to recreate from an enclosing root.
130 if (objectIndex >= newUnit->objectCount())
131 return true;
132
133 if (!isComponentRoot(newUnit, objectIndex))
134 return false;
135
136 if (newUnit->objectHasDeferredBindings(objectIndex))
137 return false;
138
139 return !hasChangedNonCompositeBaseType(oldUnit, newUnit, objectIndex);
140}
141
142// Re-resolve the object's precomputed binding-target table against its relinked property cache.
143static void refreshBindingPropertyData(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &cu,
144 int objectIndex, const QQmlPropertyCache::ConstPtr &cache)
145{
146 QList<QV4::CompiledData::BindingPropertyData> &table =
147 cu->baseCompilationUnit()->bindingPropertyDataPerObject;
148 if (objectIndex >= table.size())
149 return;
150
151 QV4::CompiledData::BindingPropertyData &bindingData = table[objectIndex];
152
153 const QV4::CompiledData::Object *obj = cu->objectAt(objectIndex);
154 const QQmlPropertyResolver resolver(cache);
155
156 const QV4::CompiledData::Binding *binding = obj->bindingTable();
157 for (qsizetype i = 0, end = bindingData.size(); i < end; ++i, ++binding) {
158 if (!bindingData.at(i))
159 continue;
160
161 Q_ASSERT(i < obj->nBindings);
162 const QString name = BindingPatchContext::targetPropertyName(cu, objectIndex, binding);
163 if (name.isEmpty())
164 continue;
165
166 if (cu->bindingIsSignalHandlerExpression(objectIndex, i)
167 || cu->bindingIsSignalHandlerObject(objectIndex, i)) {
168 bindingData[i] = resolver.signal(
169 *QQmlSignalNames::anyHandlerNameToSignalName(name), nullptr,
170 QQmlPropertyResolver::IgnoreRevision);
171 } else if (cu->bindingIsPropertyObserver(objectIndex, i)) {
172 bindingData[i] = resolver.property(
173 *QQmlSignalNames::changedHandlerNameToPropertyName(name), nullptr,
174 QQmlPropertyResolver::IgnoreRevision);
175 } else {
176 bindingData[i] = resolver.property(
177 name, nullptr, QQmlPropertyResolver::IgnoreRevision);
178 }
179 }
180}
181
182// Ensure the property cache at objectIndex in cu is derived from actualParent, the cache we just
183// used for the level below it in the VME chain.
185relinkCache(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &cu, int objectIndex,
186 const QQmlPropertyCache::ConstPtr &actualParent)
187{
188 QQmlPropertyCacheVector *caches = cu->propertyCachesPtr();
189 QQmlPropertyCache::ConstPtr cache = caches->at(objectIndex);
190
191 // The bottom-most composite level's own base is a non-composite (C++) type. That base never
192 // changes across a reload since a changed non-composite base is rejected by
193 // hasChangedNonCompositeBaseType.
194 if (!actualParent)
195 return cache;
196
197 // A type that needs no VME meta-object of its own reuses its base type's property cache.
198 if (!caches->needsVMEMetaObject(objectIndex)) {
199 if (cache != actualParent) {
200 caches->set(objectIndex, actualParent);
201 refreshBindingPropertyData(cu, objectIndex, actualParent);
202 }
203 return actualParent;
204 }
205
206 // A type with its own cache derived from the base: re-derive it from the relinked base so its
207 // inherited offsets match the (possibly changed) base layout.
208 if (cache->parent() != actualParent) {
209 cache = cache->rebased(actualParent);
210 caches->set(objectIndex, cache);
211 refreshBindingPropertyData(cu, objectIndex, cache);
212 }
213
214 return cache;
215}
216
217// Re-link every property cache in cu that (transitively) derives oldBaseCache so it derives
218// newBaseCache instead.
219static void relinkDerivedCaches(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &cu,
220 const QQmlPropertyCache::ConstPtr &oldBaseCache,
221 const QQmlPropertyCache::ConstPtr &newBaseCache)
222{
223 QQmlPropertyCacheVector *caches = cu->propertyCachesPtr();
224 Q_ASSERT(caches);
225
226 QHash<const QQmlPropertyCache *, QQmlPropertyCache::ConstPtr> replacements;
227 replacements.insert(oldBaseCache.data(), newBaseCache);
228
229 for (bool changed = true; changed;) {
230 changed = false;
231 for (int i = 0, end = caches->count(); i < end; ++i) {
232 const QQmlPropertyCache::ConstPtr cache = caches->at(i);
233 if (!cache)
234 continue;
235
236 const auto it = replacements.constFind(cache->parent().data());
237 if (it == replacements.constEnd())
238 continue;
239
240 const QQmlPropertyCache::ConstPtr rebased = cache->rebased(*it);
241 replacements.insert(cache.data(), rebased);
242 caches->set(i, rebased);
243 refreshBindingPropertyData(cu, i, rebased);
244 changed = true;
245 }
246 }
247}
248
256
257// Walk the type resolution chain starting from the object at cuIndex in unit,
258// collecting all composite (QML-defined) base type levels. Returns them ordered
259// deepest-first (e.g., grandparent before parent).
261collectCompositeLevels(const CompositeLevel &instanceLevel,
262 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
263 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
264{
265 std::vector<CompositeLevel> levels;
266
267 auto currentUnit = instanceLevel.newCu;
268 const QV4::CompiledData::Object *obj = currentUnit->objectAt(instanceLevel.objectIndex);
269 const QV4::ResolvedTypeReference *typeRef =
270 currentUnit->resolvedType(obj->inheritedTypeNameIndex);
271
272 while (typeRef && typeRef->type().isComposite()) {
273 QQmlRefPointer<QV4::ExecutableCompilationUnit> cu = typeRef->isSelfReference()
274 ? currentUnit
275 : currentUnit->engine->executableCompilationUnit(typeRef->compilationUnit());
276 Q_ASSERT(cu);
277
278 // Replace the old unit with the new one wherever it occurs
279 const auto oldCu = cu;
280 if (cu == oldUnit)
281 cu = newUnit;
282
283 int rootIndex;
284 QString icName;
285 if (typeRef->type().isInlineComponent()) {
286 icName = typeRef->type().elementName();
287 rootIndex = cu->inlineComponentId(icName);
288 } else {
289 rootIndex = 0;
290 }
291
292 levels.push_back({ oldCu, cu, rootIndex, icName, nullptr });
293
294 if (rootIndex < 0 || rootIndex >= cu->objectCount())
295 break;
296
297 // Walk deeper into the base type
298 const QV4::CompiledData::Object *rootObj = cu->objectAt(rootIndex);
299 typeRef = cu->resolvedType(rootObj->inheritedTypeNameIndex);
300 currentUnit = cu;
301 }
302
303 return levels;
304}
305
306// Rebuilding a component root replaces the contexts it is the context object of. Instances of
307// inner components created in such a context still have it as parent context. When rebuilding
308// those, we have to parent their new contexts to the replacements, or they keep resolving ids
309// against the old context, whose objects have been retired.
316
318currentContext(const QQmlRefPointer<QQmlContextData> &context,
319 const ReplacedContexts &replacedContexts)
320{
321 const auto it = replacedContexts.constFind(context.data());
322 return it == replacedContexts.constEnd() ? context : it->newContext;
323}
324
325// The contexts of the composite levels of object, in the order of collectCompositeLevels().
327levelContexts(QObject *object, const QQmlRefPointer<QQmlContextData> &outerContext)
328{
329 QVarLengthArray<QQmlRefPointer<QQmlContextData>, 4> contexts;
330 for (QQmlRefPointer<QQmlContextData> ctx(QQmlData::get(object)->context); ctx;
331 ctx = ctx->linkedContext()) {
332 if (ctx != outerContext && ctx->contextObject() == object)
333 contexts.append(ctx);
334 }
335
336 // The linked contexts are ordered from the deepest base type to the most derived one.
337 std::reverse(contexts.begin(), contexts.end());
338
339 return contexts;
340}
341
342// cuIndex is not necessarily the "outermost" index. There may be levels above oldUnit.
343// Those have to be preserved/rebuilt.
344static void rebuildObject(QObject *object, int cuIndex,
345 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
346 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit,
347 ReplacedContexts *replacedContexts)
348{
349 // If the object's index doesn't exist in the new CU, it's obsolete.
350 if (cuIndex >= newUnit->objectCount())
351 return;
352
353 QQmlData *ddata = QQmlData::get(object);
354 Q_ASSERT(ddata);
355
356 QQmlRefPointer<QQmlContextData> outerContext =
357 QQmlRefPointer<QQmlContextData>(ddata->outerContext);
358
359 // If the object has no context, or is scheduled for deletion, it's half-dead already.
360 if (!ddata->context || !outerContext || !outerContext->isValid() || ddata->isQueuedForDeletion)
361 return;
362
363 CompositeLevel instanceLevel{ ddata->compilationUnit,
364 ddata->compilationUnit == oldUnit ? newUnit
365 : ddata->compilationUnit,
366 ddata->cuObjectIndex, QString(), outerContext };
367
368 // If the object doesn't exist anymore in the new CU it will be deleted via GC.
369 // Nothing to do here.
370 if (instanceLevel.objectIndex >= instanceLevel.newCu->objectCount())
371 return;
372
373 std::vector<CompositeLevel> levels = collectCompositeLevels(instanceLevel, oldUnit, newUnit);
374
375 // Build the set of compilation units that participate in this rebuild.
376 // Bindings from these CUs are "internal" and will be re-created by repopulateBindings.
377 std::vector<CompositeLevel> internalUnits;
378 internalUnits.push_back(instanceLevel);
379 for (const auto &level : levels)
380 internalUnits.push_back(level);
381
382 BindingPatchContext patchCtx(object, ddata->compilationUnit, ddata->cuObjectIndex);
383 QDuplicateTracker<QObject *> seenChildren;
384 patchCtx.stashExternalState(internalUnits, &seenChildren);
385 // Collect the CUs whose children will be recreated by repopulateBindings.
386 // This is oldUnit (being replaced) and the composite-level CUs.
387 // NOT newUnit (already-rebuilt objects pointing here must be preserved)
388 // and NOT instanceLevel.cu (its repopulateBindings only sets bindings, not children).
389 std::vector<QQmlRefPointer<QV4::ExecutableCompilationUnit>> unitsToUnparent;
390 unitsToUnparent.push_back(oldUnit);
391 for (const auto &level : levels)
392 unitsToUnparent.push_back(level.oldCu);
393 patchCtx.reset(unitsToUnparent, internalUnits);
394
395 QV4::ExecutionEngine *v4 = newUnit->engine;
396 Q_ASSERT(v4);
397 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(v4);
398 Q_ASSERT(enginePrivate);
399
400 const QVarLengthArray<QQmlRefPointer<QQmlContextData>, 4> oldLevelContexts =
401 levelContexts(object, outerContext);
402
403 if (outerContext->contextObject() == object) {
404 const QQmlRefPointer<QQmlContextData> oldOuterContext = outerContext;
405 oldOuterContext->setContextObject(nullptr);
406 outerContext = enginePrivate->createComponentRootContext(
407 instanceLevel.newCu, currentContext(oldOuterContext->parent(), *replacedContexts),
408 instanceLevel.objectIndex);
409 outerContext->setContextObject(object);
410 instanceLevel.context = outerContext;
411 replacedContexts->insert(oldOuterContext.data(), { oldOuterContext, outerContext });
412 }
413
414 for (QQmlRefPointer<QQmlContextData> ctx(ddata->context); ctx; ctx = ctx->linkedContext()) {
415 if (ctx->contextObject() == object)
416 ctx->setContextObject(nullptr);
417 }
418
419 ddata->clear();
420
421 // clear() forgets where the object is declared, which only creating an object sets. The
422 // engine debug service finds objects by it and reports it: take it from the declaration in
423 // the unit the object is rebuilt from.
424 if (const QV4::CompiledData::Object *declaration =
425 instanceLevel.newCu->objectAt(instanceLevel.objectIndex)) {
426 ddata->lineNumber = declaration->location.line();
427 ddata->columnNumber = declaration->location.column();
428 }
429
430 QObjectPrivate *objectPrivate = QObjectPrivate::get(object);
431 delete std::exchange(objectPrivate->metaObject, nullptr);
432
433 QQmlRefPointer<QQmlContextData> levelContext = outerContext;
434 qsizetype replacedLevels = 0;
435 for (qsizetype i = 0, end = levels.size(); i < end; ++i) {
436 CompositeLevel &level = levels[i];
437 levelContext = level.context = enginePrivate->createComponentRootContext(
438 level.newCu, levelContext, level.objectIndex);
439 levelContext->setContextObject(object);
440
441 // The base type chain may have changed. Only match the old contexts up to the first
442 // level that differs.
443 if (i >= oldLevelContexts.size() || i != replacedLevels)
444 continue;
445 const QQmlRefPointer<QQmlContextData> &oldLevelContext = oldLevelContexts[i];
446 const QQmlRefPointer<QV4::ExecutableCompilationUnit> oldLevelCu =
447 oldLevelContext->typeCompilationUnit();
448 if ((oldLevelCu == oldUnit ? newUnit : oldLevelCu) != level.newCu)
449 continue;
450 replacedContexts->insert(oldLevelContext.data(), { oldLevelContext, levelContext });
451 ++replacedLevels;
452 }
453
454 if (outerContext->contextObject() == object) {
455 ddata->ownContext = outerContext;
456 ddata->context = outerContext.data();
457 if (!levels.empty())
458 ddata->context->setLinkedContext(levels.front().context);
459 } else if (!levels.empty()) {
460 ddata->ownContext = levels.back().context;
461 ddata->context = ddata->ownContext.data();
462 if (levels.size() > 1)
463 ddata->context->setLinkedContext(levels.front().context);
464 } else {
465 // Non-root child: doesn't own a context. Re-link to the parent context.
466 ddata->context = outerContext.data();
467 }
468
469 // Build the VME meta-object chain base-first, relinking each level's property cache to the
470 // actual (possibly freshly reloaded) parent cache we just used, so the whole chain's offsets
471 // stay consistent even when a composite base type's layout changed on reload.
472 QQmlPropertyCache::ConstPtr parentCache;
473 for (auto it = levels.crbegin(), end = levels.crend(); it != end; ++it) {
474 it->context->addOwnedObject(ddata);
475 QQmlPropertyCache::ConstPtr cache = relinkCache(it->newCu, it->objectIndex, parentCache);
476 if (it->newCu->propertyCachesPtr()->needsVMEMetaObject(it->objectIndex))
477 new QQmlVMEMetaObject(v4, object, cache, it->newCu, it->objectIndex);
478 parentCache = cache;
479 }
480
481 outerContext->addOwnedObject(ddata);
482 if (QQmlPropertyCacheVector *caches = instanceLevel.newCu->propertyCachesPtr();
483 caches->count() > instanceLevel.objectIndex) {
484 QQmlPropertyCache::ConstPtr cache =
485 relinkCache(instanceLevel.newCu, instanceLevel.objectIndex, parentCache);
486 if (caches->needsVMEMetaObject(instanceLevel.objectIndex))
487 new QQmlVMEMetaObject(v4, object, cache, instanceLevel.newCu, instanceLevel.objectIndex);
488 }
489
490 // Repopulate bindings at each composite level (deepest first).
491 // This sets up functions, evaluates bindings (creating child objects), etc.
492 // The object may get queued for deletion as result of some bindings.
493 // In that case we have to abort.
494 for (auto it = levels.crbegin(), end = levels.crend(); it != end && !ddata->isQueuedForDeletion;
495 ++it) {
496 QQmlObjectCreator creator(it->context, it->newCu, outerContext, it->icName, nullptr);
497 creator.repopulateBindings(it->objectIndex, object, it->context,
498 QQmlObjectCreator::InitFlag::IsContextObject
499 | QQmlObjectCreator::InitFlag::IsDocumentRoot);
500
501 QQmlInstantiationInterrupt interrupt;
502 creator.finalize(interrupt);
503 }
504
505 // The object may get queued for deletion as result of some bindings.
506 // In that case don't touch it any further.
507 if (!ddata->isQueuedForDeletion) {
508 // Repopulate bindings at the instance level in the parent CU.
509 QQmlObjectCreator creator(instanceLevel.context, instanceLevel.newCu, outerContext, QString(),
510 nullptr);
511 creator.repopulateBindings(instanceLevel.objectIndex, object, outerContext,
512 QQmlObjectCreator::InitFlag::None);
513 QQmlInstantiationInterrupt interrupt;
514 creator.finalize(interrupt);
515
516 // Restore externally set bindings and values that were stashed before reset.
517 // First refresh child object pointers — they may have been replaced during rebuild.
518 patchCtx.refreshObjects();
519 patchCtx.restoreExternalState();
520 }
521}
522
524static BindingKind bindingKind(const QV4::CompiledData::Binding *binding)
525{
526 switch (binding->type()) {
527 case QV4::CompiledData::Binding::Type_Script:
528 return BindingKind::Script;
529 case QV4::CompiledData::Binding::Type_Translation:
530 case QV4::CompiledData::Binding::Type_TranslationById:
532 case QV4::CompiledData::Binding::Type_Number:
533 case QV4::CompiledData::Binding::Type_Boolean:
534 case QV4::CompiledData::Binding::Type_String:
535 case QV4::CompiledData::Binding::Type_Null:
537 default:
539 }
540}
541
542// A trivial diff is one we can apply without rebuilding any VME meta-object and without
543// adding or removing objects: only existing bindings and binding-expression bodies change. Such a
544// diff is patched in place (see patchInPlace()). Everything else falls back to rebuilding roots.
545static bool changeIsTrivial(const QV4::CompiledData::Change &change,
546 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
547 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
548{
549 using ChangeType = QV4::CompiledData::ChangeType;
550 switch (change.type) {
551 // Source-location-only changes have no runtime effect.
552 case ChangeType::AliasLocationChanged:
553 case ChangeType::BindingLocationChanged:
554 case ChangeType::EnumLocationChanged:
555 case ChangeType::FunctionLocationChanged:
556 case ChangeType::ImportLocationChanged:
557 case ChangeType::InlineComponentLocationChanged:
558 case ChangeType::ObjectLocationChanged:
559 case ChangeType::PropertyLocationChanged:
560 case ChangeType::SignalLocationChanged:
561 // Internal table changes that are byproducts of recompiling expressions. They are picked up
562 // when we translate functions and remap compilation units; no structure changes.
563 case ChangeType::UnitMetadataChanged:
564 case ChangeType::ConstantAdded:
565 case ChangeType::ConstantChanged:
566 case ChangeType::ConstantRemoved:
567 case ChangeType::StringDataAdded:
568 case ChangeType::StringDataChanged:
569 case ChangeType::StringDataRemoved:
570 case ChangeType::LookupAdded:
571 case ChangeType::LookupChanged:
572 case ChangeType::LookupRemoved:
573 case ChangeType::RegExpAdded:
574 case ChangeType::RegExpChanged:
575 case ChangeType::RegExpRemoved:
576 case ChangeType::ClassAdded:
577 case ChangeType::ClassChanged:
578 case ChangeType::ClassRemoved:
579 case ChangeType::BlockAdded:
580 case ChangeType::BlockChanged:
581 case ChangeType::BlockRemoved:
582 case ChangeType::TemplateObjectAdded:
583 case ChangeType::TemplateObjectChanged:
584 case ChangeType::TemplateObjectRemoved:
585 case ChangeType::JSClassAdded:
586 case ChangeType::JSClassChanged:
587 case ChangeType::JSClassRemoved:
588 case ChangeType::TranslationDataAdded:
589 case ChangeType::TranslationDataChanged:
590 case ChangeType::TranslationDataRemoved:
591 return true;
592 case ChangeType::FunctionChanged:
593 // A recompiled function body at a stable index. Binding/handler expressions pick up the new
594 // body via refreshBindings(), VME methods via refreshVmeMethods(). Neither needs a rebuild,
595 // so a function change is trivial regardless of whether the function backs a binding or a
596 // method.
597 return true;
598 case ChangeType::BindingChanged: {
599 Q_ASSERT(change.objectIndex >= 0);
600 Q_ASSERT(change.objectIndex < oldUnit->objectCount());
601 Q_ASSERT(change.objectIndex < newUnit->objectCount());
602
603 const QV4::CompiledData::Object *oldObj = oldUnit->objectAt(change.objectIndex);
604 Q_ASSERT(change.index < oldObj->nBindings);
605 const QV4::CompiledData::Binding *oldBinding = oldObj->bindingTable() + change.index;
606
607 const QV4::CompiledData::Object *newObj = newUnit->objectAt(change.objectIndex);
608 Q_ASSERT(change.index < newObj->nBindings);
609 const QV4::CompiledData::Binding *newBinding = newObj->bindingTable() + change.index;
610
611 const BindingKind oldKind = bindingKind(oldBinding);
612 const BindingKind newKind = bindingKind(newBinding);
613 if (oldKind == BindingKind::Unpatchable || newKind == BindingKind::Unpatchable)
614 return false;
615
616 // We cannot swap a binding for one of a different kind, yet: turning a literal into a
617 // script or translation binding (or vice versa) would have to install or drop a live
618 // binding, and we cannot recompile a script binding into a translation binding either.
619 // TODO: Fix this.
620 if (oldKind != newKind)
621 return false;
622
623 // A BindingChanged at a stable index can also mean the binding was moved to a different
624 // target property (its propertyNameIndex changed). In-place patching cannot relocate a
625 // binding: it would have to remove it from the old property and install it on the new one.
626 // That is structural, so fall back to the rebuild path.
627
628 return BindingPatchContext::targetPropertyName(oldUnit, change.objectIndex, oldBinding)
629 == BindingPatchContext::targetPropertyName(newUnit, change.objectIndex, newBinding);
630 }
631 default:
632 // ObjectAdded/Removed/Changed, Binding Added/Removed, Property/Signal/Alias/Enum changes,
633 // Function Added/Removed, Import/InlineComponent/RequiredPropertyExtraData changes:
634 // structural, need a rebuild.
635 return false;
636 }
637}
638
639static bool isTrivialDiff(const QV4::CompiledData::CompilationUnitDiff &diff,
640 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
641 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
642{
643 return std::all_of(diff.changes.cbegin(), diff.changes.cend(),
644 [&](const QV4::CompiledData::Change &change) {
645 return changeIsTrivial(change, oldUnit, newUnit);
646 });
647}
648
649// Re-apply changed literal bindings. A literal value is written only if the property still holds
650// the value the old unit assigned; anything else is an external override we must leave untouched.
651// Changed script bindings carry no literal value and are handled by function translation instead.
652//
653// The binding may live on a value-type group sub-object (font.pixelSize) or an attached object
654// (Keys.enabled), which has no standalone QObject at its compilation-unit index. We use
655// BindingPatchContext to map every compilation-unit object index reachable from a live instance to
656// the owning QObject and the property-name prefix to address it.
657static void patchConstantBindings(const std::vector<QObject *> &objects,
658 const QV4::CompiledData::CompilationUnitDiff &diff,
659 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
660 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
661{
662 for (QObject *object : objects) {
663 // Skip half-dead objects, mirroring rebuildObject(): an invalidated context means the
664 // object is on its way out and must not be patched.
665 QQmlData *ddata = QQmlData::get(object);
666 if (!ddata->context || !ddata->outerContext || !ddata->outerContext->isValid()
667 || ddata->isQueuedForDeletion) {
668 continue;
669 }
670
671 QVarLengthArray<BindingPatchContext, 4> contexts;
672 for (int index : objectIndices(object, oldUnit))
673 contexts.append(BindingPatchContext(object, oldUnit, index));
674
675 for (const QV4::CompiledData::Change &change : diff.changes) {
676 if (change.type != QV4::CompiledData::ChangeType::BindingChanged)
677 continue;
678
679 for (BindingPatchContext &context : contexts) {
680 if (context.applyBindingChange(newUnit, change))
681 break;
682 }
683 }
684 }
685}
686
687// Point every live object that still references oldUnit at newUnit, in both its ddata and its
688// VME meta-object chain. Used after in-place patching, which keeps all object indices stable.
689//
690// The ddata compilation unit and the VME chain are remapped independently: a composite-type
691// instance can carry oldUnit in its VME chain while its ddata->compilationUnit is a different
692// executable unit (the "topmost", possibly inaddressible, instantiation).
693static void remapObjectsToNewUnit(const std::vector<QObject *> &objects,
694 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
695 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
696{
697 for (QObject *object : objects) {
698 QQmlData *ddata = QQmlData::get(object);
699
700 // A QQmlComponent (e.g. an explicit or implicit `delegate:`) caches the compilation unit it
701 // instantiates from. Remap it so future create() calls produce instances from newUnit.
702 if (QQmlComponent *component = qobject_cast<QQmlComponent *>(object)) {
703 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(component);
704 if (cp->compilationUnit() == oldUnit)
705 cp->setCompilationUnit(newUnit);
706 }
707
708 // Remap the ddata compilation unit if it points at oldUnit. An object whose index is out
709 // of range in the new CU is obsolete (it belonged to the old type definition); leave its
710 // ddata alone and let the VME nulling below retire it.
711 if (ddata->compilationUnit == oldUnit && ddata->cuObjectIndex < newUnit->objectCount())
712 ddata->compilationUnit = newUnit;
713
714 if (!ddata->hasVMEMetaObject)
715 continue;
716
717 // Remap (or retire) every VME meta-object in the chain that points at oldUnit.
718 auto *mainVme = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(object)->metaObject);
719 for (QQmlVMEMetaObject *vmeMeta = mainVme; vmeMeta;
720 vmeMeta = vmeMeta->parentVMEMetaObject()) {
721 if (vmeMeta->compilationUnit() != oldUnit)
722 continue;
723 if (vmeMeta->qmlObjectId() < newUnit->objectCount()) {
724 vmeMeta->setCompilationUnit(newUnit);
725
726 // Give the live instance the reloaded type's identity. The instance must present
727 // newUnit's property cache.
728 const QQmlPropertyCache::ConstPtr newCache =
729 newUnit->propertyCachesPtr()->at(vmeMeta->qmlObjectId());
730 if (newCache) {
731 vmeMeta->setPropertyCache(newCache);
732 if (vmeMeta == mainVme)
733 ddata->propertyCache = newCache;
734 }
735 } else {
736 // Obsolete: null it so stale alias lookups (triggered by refreshBindings) safely
737 // return nullptr from findCompiledObject() instead of asserting.
738 vmeMeta->setCompilationUnit(nullptr);
739 }
740 }
741 }
742}
743
744// Returns true if the object is instantiated as a component root of the old unit that we can
745// rebuild in place (see canRebuildComponentRootInPlace()), or as a component root of a different,
746// unchanged unit. The latter is rebuilt against its own unit. Otherwise, for example if the object
747// is not a component root at all or not even created from QML, return false.
749 QObject *object,
750 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
751 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
752{
753 bool foundComponentRoot = false;
754 for (int index : objectIndices(object, oldUnit)) {
755 if (index >= oldUnit->objectCount() || !isComponentRoot(oldUnit, index))
756 continue;
757 if (!canRebuildComponentRootInPlace(oldUnit, newUnit, index))
758 return false;
759 foundComponentRoot = true;
760 }
761
762 if (foundComponentRoot)
763 return true;
764
765 const QQmlData *ddata = QQmlData::get(object);
766 return ddata && ddata->compilationUnit && ddata->compilationUnit != oldUnit
767 && isComponentRoot(ddata->compilationUnit, ddata->cuObjectIndex);
768}
769
770// A component root whose non-composite base type changed or that carries deferred bindings cannot
771// be rebuilt in place. Instead we travel the scope hierarchy upwards to the enclosing component
772// root that instantiates it and rebuild that: its reset() + repopulateBindings() recreates the
773// problematic object from scratch, as a fresh QObject. If that enclosing root is problematic, too,
774// we keep travelling up. Returns the object to rebuild, or nullptr if we reach the root scope
775// without finding a rebuildable enclosing root.
777 QObject *object,
778 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
779 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
780{
781 Q_ASSERT(object);
782
783 const QQmlData *ddata = QQmlData::get(object);
784 if (!ddata)
785 return nullptr;
786
787 // Walk up the context hierarchy. The root of a component instance is the context object of
788 // its own context. Contexts may also have no context object or one that is not a component
789 // root at all (e.g. a delegate model's item). If we reach the root context, we give up.
790 for (QQmlRefPointer<QQmlContextData> context(ddata->outerContext); context;
791 context = context->parent()) {
792 QObject *outer = context->contextObject();
793 if (outer && outer != object && isRebuildableComponentRoot(outer, oldUnit, newUnit))
794 return outer;
795 }
796
797 return nullptr;
798}
799
800// TODO: This is dangerous. We are manipulating compilation units exposed to multiple
801// engines on potentially multiple threads.
803 QV4::ExecutionEngine *engine,
804 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &oldUnit,
805 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &newUnit)
806{
807 const auto units = engine->compilationUnits();
808 for (const auto &cu : units) {
809 for (auto *typeRef : std::as_const(cu->baseCompilationUnit()->resolvedTypes)) {
810 if (typeRef->isSelfReference())
811 continue;
812 if (typeRef->compilationUnit() != oldUnit)
813 continue;
814
815 typeRef->setCompilationUnit(newUnit);
816
817 QQmlPropertyCache::ConstPtr newCache;
818 const QQmlType type = typeRef->type();
819 if (type.isInlineComponent()) {
820 if (const int icId = newUnit->inlineComponentId(type.elementName()); icId >= 0)
821 newCache = newUnit->propertyCaches.at(icId);
822 } else {
823 newCache = newUnit->rootPropertyCache();
824 }
825
826 if (!newCache)
827 continue;
828
829 const QQmlPropertyCache::ConstPtr oldCache = typeRef->typePropertyCache();
830 typeRef->setTypePropertyCache(newCache);
831 relinkDerivedCaches(cu, oldCache, newCache);
832 }
833 }
834}
835
836// Re-create the VME method function objects from newUnit so that an object's methods follow the
837// unit remap, mirroring what refreshBindings() does for QQmlJavaScriptExpression-based bindings.
838static void refreshVmeMethods(const std::vector<QObject *> &objects,
839 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
840{
841 QV4::ExecutionEngine *v4 = newUnit->engine;
842 QV4::Scope scope(v4);
843 QV4::ScopedValue function(scope);
844
845 for (QObject *object : objects) {
846 QQmlData *ddata = QQmlData::get(object);
847 if (!ddata || !ddata->hasVMEMetaObject)
848 continue;
849
850 auto *mainVme = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(object)->metaObject);
851 for (QQmlVMEMetaObject *vme = mainVme; vme; vme = vme->parentVMEMetaObject()) {
852 const auto cu = vme->compilationUnit();
853 if (cu != newUnit)
854 continue;
855
856 const QQmlRefPointer<QQmlContextData> context = vme->contextData();
857 if (!context)
858 continue;
859
860 const int objectIndex = vme->qmlObjectId();
861 if (objectIndex < 0 || objectIndex >= cu->objectCount())
862 continue;
863
864 const QV4::CompiledData::Object *obj = cu->objectAt(objectIndex);
865 const QQmlPropertyCache::ConstPtr cache = cu->propertyCachesPtr()->at(objectIndex);
866 if (!cache)
867 continue;
868
869 QV4::Scoped<QV4::QmlContext> qmlContext(
870 scope, QV4::QmlContext::create(v4->rootContext(), context, object));
871
872 const quint32_le *functionIdx = obj->functionOffsetTable();
873 for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIdx) {
874 QV4::Function *runtimeFunction = cu->runtimeFunctions[*functionIdx];
875 const QString name = runtimeFunction->name()->toQString();
876
877 const QQmlPropertyData *property = cache->property(name, object, context);
878 if (!property || !property->isVMEFunction())
879 continue;
880
881 // Mirror QQmlObjectCreator::setupFunctions(): generators need their own creator.
882 function = runtimeFunction->isGenerator()
883 ? QV4::GeneratorFunction::create(qmlContext, runtimeFunction)
884 : QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
885 mainVme->setVmeMethod(property->coreIndex(), function);
886 }
887 }
888 }
889}
890
891PatchResult applyDiff(std::vector<QObject *> &objects,
892 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
893 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
894{
895 // Ensure the new CU's runtime data (strings, lookups, functions) is populated.
896 if (!newUnit->runtimeStrings)
897 newUnit->populate();
898
899 // Fast path: if only existing bindings changed -- nothing structural about any VME
900 // meta-object, and no objects added or removed -- patch the affected objects in place. We
901 // leave every object and its VME meta-object where it is, re-apply the changed literal
902 // bindings (patchConstantBindings), point the objects at newUnit (remapObjectsToNewUnit), and
903 // let the in-place refreshBindings() translate the still-live expressions' functions to
904 // newUnit. Because no functions are added or removed in a trivial diff, function indices are
905 // stable, which is what makes that same-index translation correct.
906 const QV4::CompiledData::CompilationUnitDiff diff =
907 QV4::CompiledData::diffCompilationUnits(oldUnit->unitData(), newUnit->unitData());
908 if (diff.success && isTrivialDiff(diff, oldUnit, newUnit)) {
909 patchConstantBindings(objects, diff, oldUnit, newUnit);
910 remapObjectsToNewUnit(objects, oldUnit, newUnit);
911 refreshVmeMethods(objects, newUnit);
913 }
914
915 // Otherwise we rebuild whole component roots: the document root (index 0) and any
916 // inline-component roots. We don't touch other objects directly. Rebuilding a root runs a
917 // full reset() + repopulateBindings() that cascades down the entire instantiation (and
918 // through its composite base levels), recreating every object below it. Since every
919 // non-root object is, by construction, a descendant of a component root, this is
920 // sufficient. A component root that cannot be rebuilt in place - its C++ base type changed, or
921 // it carries deferred bindings (see below) - is handled by rebuilding the enclosing component
922 // root instead, which may live in a different compilation unit and recreates the root as a
923 // fresh object (of the new C++ class, with its deferred bindings armed and executed).
924 //
925 // A consequence worth remembering: because every live object is recreated, every
926 // QQmlJavaScriptExpression on those objects is recreated too, freshly bound to newUnit. By
927 // the time refreshBindings() runs, the only expressions still referencing oldUnit are the
928 // dead, detached leftovers of the resets. That is why the rebuild path's refreshBindings()
929 // can simply disable (null) them instead of remapping them to newUnit.
930 // A rebuild target carries the compilation units to rebuild it with. Usually that's oldUnit ->
931 // newUnit. But when we travel up to an enclosing component root in a *different* compilation
932 // unit (because a root's base type changed), that unit is itself unchanged: we rebuild it
933 // against itself (a full re-instantiation of its children) after redirecting the resolved type
934 // references, so the problematic object is recreated with its new C++ base type.
935 std::vector<RebuildTarget> rebuild;
936 QSet<QObject *> rebuildSet;
937 bool redirectedTypes = false;
938
939 const auto addRebuild = [&](QObject *object, int index,
940 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldCu,
941 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newCu) {
942 if (rebuildSet.contains(object))
943 return;
944 rebuildSet.insert(object);
945 rebuild.push_back({ object, index, oldCu, newCu });
946 };
947
948 for (QObject *object : objects) {
949 const QVarLengthArray<int, 4> indices = objectIndices(object, oldUnit);
950 for (int index : indices) {
951 // Objects instantiated by an enclosing component (indices beyond the CU) are recreated
952 // when that enclosing root is rebuilt, so we don't record them here. Neither do we
953 // record any other objects but component roots. Instances of inner components are not
954 // recreated by rebuilding the enclosing component. Therefore, we record their roots.
955 if (index >= oldUnit->objectCount() || !isComponentRoot(oldUnit, index))
956 continue;
957
958 // A component root whose own non-composite base type changed, which carries deferred
959 // bidnings, or which isn't a component root anymore cannot be rebuilt in place. Rebuild
960 // the enclosing component root instead, so that the object is recreated from scratch
961 // as an instance of the new C++ class with new deferred bindings. A surviving instance
962 // of an inner component stays on oldUnit then. Travelling up the scope hierarchy this
963 // way only fails once we hit the root scope.
964 if (!canRebuildComponentRootInPlace(oldUnit, newUnit, index)) {
965 QObject *outer = outerRebuildTarget(object, oldUnit, newUnit);
966 if (!outer)
967 return PatchResult::Failed;
968
969 // The enclosing root may recreate the object from a different compilation unit
970 // (the object was instantiated as an external type). Point that unit at newUnit
971 // first, so the recreated object gets the new definition.
972 if (!redirectedTypes) {
973 redirectResolvedTypeReferences(newUnit->engine, oldUnit->baseCompilationUnit(),
974 newUnit->baseCompilationUnit());
975 redirectedTypes = true;
976 }
977
978 // Rebuild the enclosing root against its own compilation unit (or newUnit, if that
979 // root lives in oldUnit itself, e.g. an inline component). The unchanged outer unit
980 // re-instantiates its children, recreating the problematic object.
981 const QQmlRefPointer<QV4::ExecutableCompilationUnit> outerOld =
982 QQmlData::get(outer)->compilationUnit;
983 const QQmlRefPointer<QV4::ExecutableCompilationUnit> outerNew =
984 outerOld->baseCompilationUnit() == oldUnit->baseCompilationUnit()
985 ? newUnit
986 : outerOld;
987 addRebuild(outer, QQmlData::get(outer)->cuObjectIndex, outerOld, outerNew);
988 break;
989 }
990
991 addRebuild(object, indices.first(), oldUnit, newUnit);
992 break;
993 }
994 }
995
996 // Sort by ascending cuIndex so that parent objects are rebuilt before their
997 // children. A parent's reset() properly retires children (they still point to
998 // oldUnit) and repopulateBindings recreates them. This avoids leaking orphaned
999 // children and naturally handles cases where the context needs more ID slots.
1000 std::sort(rebuild.begin(), rebuild.end(),
1001 [](const RebuildTarget &a, const RebuildTarget &b) { return a.index < b.index; });
1002
1003 // Pre-compute which objects to skip: if an ancestor is also in the rebuild
1004 // list, the child will be properly retired and recreated during the ancestor's
1005 // rebuild. Rebuilding it individually would be wasted work on a stale pointer.
1006 QSet<QObject *> skip;
1007 for (const RebuildTarget &target : rebuild) {
1008 const QObjectList &children = target.object->children();
1009 for (QObject *child : children)
1010 skip.insert(child);
1011 }
1012
1013 ReplacedContexts replacedContexts;
1014 for (const RebuildTarget &target : rebuild) {
1015 if (skip.contains(target.object))
1016 continue;
1017
1018 rebuildObject(target.object, target.index, target.oldCu, target.newCu, &replacedContexts);
1019 }
1020
1021 // Everything lexically inside the rebuilt components has been recreated or rebuilt now. Whatever
1022 // was instantiated by oldUnit and survived without being rebuilt is obsolete. We cannot remap it
1023 // to newUnit by index since indices may have shifted. Retire it.
1024 for (QObject *object : objects) {
1025 const QQmlData *ddata = QQmlData::get(object);
1026 if (ddata->compilationUnit != oldUnit || ddata->isQueuedForDeletion)
1027 continue;
1028 BindingPatchContext::clearBindingsRecursive(object);
1029 BindingPatchContext::retireObject(object);
1030 }
1031
1032 return PatchResult::Rebuilt;
1033}
1034
1035// Translate a still-live expression's function from oldUnit to the function at the same index in
1036// newUnit, or clear it if there is no newUnit. Expressions whose function does not point into
1037// oldUnit are left untouched. Same-index translation is valid because we only do this with trivial
1038// diffs that neither add nor remove functions.
1039// Returns true if the resulting function is valid afterwards (and should be re-evaluated)
1040static bool
1041translateExpressionFunction(QQmlJavaScriptExpression *expr,
1042 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
1043 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
1044{
1045 QV4::Function *f = expr->function();
1046 if (!f)
1047 return false;
1048
1049 if (f->executableCompilationUnit() != oldUnit.data())
1050 return true;
1051
1052 if (newUnit) {
1053 const qsizetype index = oldUnit->runtimeFunctions.indexOf(f);
1054 if (index >= 0 && index < newUnit->runtimeFunctions.size())
1055 expr->setFunction(newUnit->runtimeFunctions[index]);
1056 } else {
1057 expr->setFunction(nullptr);
1058 return false;
1059 }
1060
1061 return true;
1062}
1063
1065 const QQmlRefPointer<QQmlContextData> &context,
1066 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
1067 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
1068{
1069 for (auto child = context->childContexts(); child; child = child->nextChild())
1070 translateAndRefreshExpressionsRecursive(child, oldUnit, newUnit);
1071
1072 for (auto *expr = context->expressions(); expr; expr = expr->nextExpression()) {
1073 if (translateExpressionFunction(expr, oldUnit, newUnit))
1074 expr->refresh();
1075 }
1076}
1077
1078void refreshBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
1079 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
1080{
1081 translateAndRefreshExpressionsRecursive(
1082 QQmlContextData::get(oldUnit->engine->qmlEngine()->rootContext()), oldUnit, newUnit);
1083}
1084
1085} // namespace QQmlPreview
1086
1087QT_END_NAMESPACE
static std::vector< CompositeLevel > collectCompositeLevels(const CompositeLevel &instanceLevel, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static void refreshVmeMethods(const std::vector< QObject * > &objects, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
void refreshBindings(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static void rebuildObject(QObject *object, int cuIndex, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit, ReplacedContexts *replacedContexts)
static void refreshBindingPropertyData(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &cu, int objectIndex, const QQmlPropertyCache::ConstPtr &cache)
static bool canRebuildComponentRootInPlace(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit, int objectIndex)
static bool translateExpressionFunction(QQmlJavaScriptExpression *expr, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static bool isRebuildableComponentRoot(QObject *object, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static bool hasChangedNonCompositeBaseType(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit, int objectIndex)
static QVarLengthArray< int, 4 > objectIndices(QObject *object, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit)
static QQmlPropertyCache::ConstPtr relinkCache(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &cu, int objectIndex, const QQmlPropertyCache::ConstPtr &actualParent)
static void relinkDerivedCaches(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &cu, const QQmlPropertyCache::ConstPtr &oldBaseCache, const QQmlPropertyCache::ConstPtr &newBaseCache)
static BindingKind bindingKind(const QV4::CompiledData::Binding *binding)
void redirectResolvedTypeReferences(QV4::ExecutionEngine *engine, const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &oldUnit, const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &newUnit)
PatchResult applyDiff(std::vector< QObject * > &objects, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static bool isComponentRoot(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &unit, int objectIndex)
static bool isTrivialDiff(const QV4::CompiledData::CompilationUnitDiff &diff, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static QQmlRefPointer< QQmlContextData > currentContext(const QQmlRefPointer< QQmlContextData > &context, const ReplacedContexts &replacedContexts)
static QQmlPropertyCache::ConstPtr nonCompositeBaseType(const QQmlPropertyCache::ConstPtr &propertyCache)
static void translateAndRefreshExpressionsRecursive(const QQmlRefPointer< QQmlContextData > &context, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static bool changeIsTrivial(const QV4::CompiledData::Change &change, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static QObject * outerRebuildTarget(QObject *object, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static void patchConstantBindings(const std::vector< QObject * > &objects, const QV4::CompiledData::CompilationUnitDiff &diff, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
static QVarLengthArray< QQmlRefPointer< QQmlContextData >, 4 > levelContexts(QObject *object, const QQmlRefPointer< QQmlContextData > &outerContext)
static void remapObjectsToNewUnit(const std::vector< QObject * > &objects, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &oldUnit, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &newUnit)
Combined button and popup list for selecting options.
QQmlRefPointer< QV4::ExecutableCompilationUnit > newCu
QQmlRefPointer< QV4::ExecutableCompilationUnit > oldCu
QQmlRefPointer< QQmlContextData > newContext
QQmlRefPointer< QQmlContextData > oldContext