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