144 int objectIndex,
const QQmlPropertyCache::ConstPtr &cache)
146 QList<QV4::CompiledData::BindingPropertyData> &table =
147 cu->baseCompilationUnit()->bindingPropertyDataPerObject;
148 if (objectIndex >= table.size())
151 QV4::CompiledData::BindingPropertyData &bindingData = table[objectIndex];
153 const QV4::CompiledData::Object *obj = cu->objectAt(objectIndex);
154 const QQmlPropertyResolver resolver(cache);
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))
161 Q_ASSERT(i < obj->nBindings);
162 const QString name = BindingPatchContext::targetPropertyName(cu, objectIndex, binding);
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);
176 bindingData[i] = resolver.property(
177 name,
nullptr, QQmlPropertyResolver::IgnoreRevision);
262 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
263 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
265 std::vector<CompositeLevel> levels;
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);
272 while (typeRef && typeRef->type().isComposite()) {
273 QQmlRefPointer<QV4::ExecutableCompilationUnit> cu = typeRef->isSelfReference()
275 : currentUnit->engine->executableCompilationUnit(typeRef->compilationUnit());
279 const auto oldCu = cu;
285 if (typeRef->type().isInlineComponent()) {
286 icName = typeRef->type().elementName();
287 rootIndex = cu->inlineComponentId(icName);
292 levels.push_back({ oldCu, cu, rootIndex, icName,
nullptr });
294 if (rootIndex < 0 || rootIndex >= cu->objectCount())
298 const QV4::CompiledData::Object *rootObj = cu->objectAt(rootIndex);
299 typeRef = cu->resolvedType(rootObj->inheritedTypeNameIndex);
345 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
346 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit,
347 ReplacedContexts *replacedContexts)
350 if (cuIndex >= newUnit->objectCount())
353 QQmlData *ddata = QQmlData::get(object);
356 QQmlRefPointer<QQmlContextData> outerContext =
357 QQmlRefPointer<QQmlContextData>(ddata->outerContext);
360 if (!ddata->context || !outerContext || !outerContext->isValid() || ddata->isQueuedForDeletion)
363 CompositeLevel instanceLevel{ ddata->compilationUnit,
364 ddata->compilationUnit == oldUnit ? newUnit
365 : ddata->compilationUnit,
366 ddata->cuObjectIndex, QString(), outerContext };
370 if (instanceLevel.objectIndex >= instanceLevel.newCu->objectCount())
373 std::vector<CompositeLevel> levels = collectCompositeLevels(instanceLevel, oldUnit, newUnit);
377 std::vector<CompositeLevel> internalUnits;
378 internalUnits.push_back(instanceLevel);
379 for (
const auto &level : levels)
380 internalUnits.push_back(level);
382 BindingPatchContext patchCtx(object, ddata->compilationUnit, ddata->cuObjectIndex);
383 QDuplicateTracker<QObject *> seenChildren;
384 patchCtx.stashExternalState(internalUnits, &seenChildren);
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);
395 QV4::ExecutionEngine *v4 = newUnit->engine;
397 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(v4);
398 Q_ASSERT(enginePrivate);
400 const QVarLengthArray<QQmlRefPointer<QQmlContextData>, 4> oldLevelContexts =
401 levelContexts(object, outerContext);
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 });
414 for (QQmlRefPointer<QQmlContextData> ctx(ddata->context); ctx; ctx = ctx->linkedContext()) {
415 if (ctx->contextObject() == object)
416 ctx->setContextObject(
nullptr);
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();
430 QObjectPrivate *objectPrivate = QObjectPrivate::get(object);
431 delete std::exchange(objectPrivate->metaObject,
nullptr);
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);
443 if (i >= oldLevelContexts.size() || i != replacedLevels)
445 const QQmlRefPointer<QQmlContextData> &oldLevelContext = oldLevelContexts[i];
446 const QQmlRefPointer<QV4::ExecutableCompilationUnit> oldLevelCu =
447 oldLevelContext->typeCompilationUnit();
448 if ((oldLevelCu == oldUnit ? newUnit : oldLevelCu) != level.newCu)
450 replacedContexts->insert(oldLevelContext.data(), { oldLevelContext, levelContext });
454 if (outerContext->contextObject() == object) {
455 ddata->ownContext = outerContext;
456 ddata->context = outerContext.data();
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);
466 ddata->context = outerContext.data();
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);
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);
494 for (
auto it = levels.crbegin(), end = levels.crend(); it != end && !ddata->isQueuedForDeletion;
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);
501 QQmlInstantiationInterrupt interrupt;
502 creator.finalize(interrupt);
507 if (!ddata->isQueuedForDeletion) {
509 QQmlObjectCreator creator(instanceLevel.context, instanceLevel.newCu, outerContext, QString(),
511 creator.repopulateBindings(instanceLevel.objectIndex, object, outerContext,
512 QQmlObjectCreator::InitFlag::None);
513 QQmlInstantiationInterrupt interrupt;
514 creator.finalize(interrupt);
518 patchCtx.refreshObjects();
519 patchCtx.restoreExternalState();
546 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
547 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
549 using ChangeType = QV4::CompiledData::ChangeType;
550 switch (change.type) {
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:
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:
592 case ChangeType::FunctionChanged:
598 case ChangeType::BindingChanged: {
599 Q_ASSERT(change.objectIndex >= 0);
600 Q_ASSERT(change.objectIndex < oldUnit->objectCount());
601 Q_ASSERT(change.objectIndex < newUnit->objectCount());
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;
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;
611 const BindingKind oldKind = bindingKind(oldBinding);
612 const BindingKind newKind = bindingKind(newBinding);
620 if (oldKind != newKind)
628 return BindingPatchContext::targetPropertyName(oldUnit, change.objectIndex, oldBinding)
629 == BindingPatchContext::targetPropertyName(newUnit, change.objectIndex, newBinding);
694 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
695 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
697 for (QObject *object : objects) {
698 QQmlData *ddata = QQmlData::get(object);
702 if (QQmlComponent *component = qobject_cast<QQmlComponent *>(object)) {
703 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(component);
704 if (cp->compilationUnit() == oldUnit)
705 cp->setCompilationUnit(newUnit);
711 if (ddata->compilationUnit == oldUnit && ddata->cuObjectIndex < newUnit->objectCount())
712 ddata->compilationUnit = newUnit;
714 if (!ddata->hasVMEMetaObject)
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)
723 if (vmeMeta->qmlObjectId() < newUnit->objectCount()) {
724 vmeMeta->setCompilationUnit(newUnit);
728 const QQmlPropertyCache::ConstPtr newCache =
729 newUnit->propertyCachesPtr()->at(vmeMeta->qmlObjectId());
731 vmeMeta->setPropertyCache(newCache);
732 if (vmeMeta == mainVme)
733 ddata->propertyCache = newCache;
738 vmeMeta->setCompilationUnit(
nullptr);
839 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
841 QV4::ExecutionEngine *v4 = newUnit->engine;
842 QV4::Scope scope(v4);
843 QV4::ScopedValue function(scope);
845 for (QObject *object : objects) {
846 QQmlData *ddata = QQmlData::get(object);
847 if (!ddata || !ddata->hasVMEMetaObject)
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();
856 const QQmlRefPointer<QQmlContextData> context = vme->contextData();
860 const int objectIndex = vme->qmlObjectId();
861 if (objectIndex < 0 || objectIndex >= cu->objectCount())
864 const QV4::CompiledData::Object *obj = cu->objectAt(objectIndex);
865 const QQmlPropertyCache::ConstPtr cache = cu->propertyCachesPtr()->at(objectIndex);
869 QV4::Scoped<QV4::QmlContext> qmlContext(
870 scope, QV4::QmlContext::create(v4->rootContext(), context, object));
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();
877 const QQmlPropertyData *property = cache->property(name, object, context);
878 if (!property || !property->isVMEFunction())
882 function = runtimeFunction->isGenerator()
883 ? QV4::GeneratorFunction::create(qmlContext, runtimeFunction)
884 : QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
885 mainVme->setVmeMethod(property->coreIndex(), function);
892 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
893 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
896 if (!newUnit->runtimeStrings)
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);
935 std::vector<RebuildTarget> rebuild;
936 QSet<QObject *> rebuildSet;
937 bool redirectedTypes =
false;
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))
944 rebuildSet.insert(object);
945 rebuild.push_back({ object, index, oldCu, newCu });
948 for (QObject *object : objects) {
949 const QVarLengthArray<
int, 4> indices = objectIndices(object, oldUnit);
950 for (
int index : indices) {
955 if (index >= oldUnit->objectCount() || !isComponentRoot(oldUnit, index))
964 if (!canRebuildComponentRootInPlace(oldUnit, newUnit, index)) {
965 QObject *outer = outerRebuildTarget(object, oldUnit, newUnit);
967 return PatchResult::Failed;
972 if (!redirectedTypes) {
973 redirectResolvedTypeReferences(newUnit->engine, oldUnit->baseCompilationUnit(),
974 newUnit->baseCompilationUnit());
975 redirectedTypes =
true;
981 const QQmlRefPointer<QV4::ExecutableCompilationUnit> outerOld =
982 QQmlData::get(outer)->compilationUnit;
983 const QQmlRefPointer<QV4::ExecutableCompilationUnit> outerNew =
984 outerOld->baseCompilationUnit() == oldUnit->baseCompilationUnit()
987 addRebuild(outer, QQmlData::get(outer)->cuObjectIndex, outerOld, outerNew);
991 addRebuild(object, indices.first(), oldUnit, newUnit);
1000 std::sort(rebuild.begin(), rebuild.end(),
1001 [](
const RebuildTarget &a,
const RebuildTarget &b) {
return a.index < b.index; });
1006 QSet<QObject *> skip;
1007 for (
const RebuildTarget &target : rebuild) {
1008 const QObjectList &children = target.object->children();
1009 for (QObject *child : children)
1013 ReplacedContexts replacedContexts;
1014 for (
const RebuildTarget &target : rebuild) {
1015 if (skip.contains(target.object))
1018 rebuildObject(target.object, target.index, target.oldCu, target.newCu, &replacedContexts);
1024 for (QObject *object : objects) {
1025 const QQmlData *ddata = QQmlData::get(object);
1026 if (ddata->compilationUnit != oldUnit || ddata->isQueuedForDeletion)
1028 BindingPatchContext::clearBindingsRecursive(object);
1029 BindingPatchContext::retireObject(object);