145 int objectIndex,
const QQmlPropertyCache::ConstPtr &cache)
147 QList<QV4::CompiledData::BindingPropertyData> &table =
148 cu->baseCompilationUnit()->bindingPropertyDataPerObject;
149 if (objectIndex >= table.size())
152 QV4::CompiledData::BindingPropertyData &bindingData = table[objectIndex];
154 const QV4::CompiledData::Object *obj = cu->objectAt(objectIndex);
155 const QQmlPropertyResolver resolver(cache);
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))
162 Q_ASSERT(i < obj->nBindings);
163 const QString name = BindingPatchContext::targetPropertyName(cu, objectIndex, binding);
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);
177 bindingData[i] = resolver.property(
178 name,
nullptr, QQmlPropertyResolver::IgnoreRevision);
263 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
264 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
266 std::vector<CompositeLevel> levels;
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);
273 while (typeRef && typeRef->type().isComposite()) {
274 QQmlRefPointer<QV4::ExecutableCompilationUnit> cu = typeRef->isSelfReference()
276 : currentUnit->engine->executableCompilationUnit(typeRef->compilationUnit());
280 const auto oldCu = cu;
286 if (typeRef->type().isInlineComponent()) {
287 icName = typeRef->type().elementName();
288 rootIndex = cu->inlineComponentId(icName);
293 levels.push_back({ oldCu, cu, rootIndex, icName,
nullptr });
295 if (rootIndex < 0 || rootIndex >= cu->objectCount())
299 const QV4::CompiledData::Object *rootObj = cu->objectAt(rootIndex);
300 typeRef = cu->resolvedType(rootObj->inheritedTypeNameIndex);
346 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
347 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit,
348 ReplacedContexts *replacedContexts)
351 if (cuIndex >= newUnit->objectCount())
354 QQmlData *ddata = QQmlData::get(object);
357 QQmlRefPointer<QQmlContextData> outerContext =
358 QQmlRefPointer<QQmlContextData>(ddata->outerContext);
361 if (!ddata->context || !outerContext || !outerContext->isValid() || ddata->isQueuedForDeletion)
364 CompositeLevel instanceLevel{ ddata->compilationUnit,
365 ddata->compilationUnit == oldUnit ? newUnit
366 : ddata->compilationUnit,
367 ddata->cuObjectIndex, QString(), outerContext };
371 if (instanceLevel.objectIndex >= instanceLevel.newCu->objectCount())
374 std::vector<CompositeLevel> levels = collectCompositeLevels(instanceLevel, oldUnit, newUnit);
378 std::vector<CompositeLevel> internalUnits;
379 internalUnits.push_back(instanceLevel);
380 for (
const auto &level : levels)
381 internalUnits.push_back(level);
383 BindingPatchContext patchCtx(object, ddata->compilationUnit, ddata->cuObjectIndex);
384 QDuplicateTracker<QObject *> seenChildren;
385 patchCtx.stashExternalState(internalUnits, &seenChildren);
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);
396 QV4::ExecutionEngine *v4 = newUnit->engine;
398 QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(v4);
399 Q_ASSERT(enginePrivate);
401 const QVarLengthArray<QQmlRefPointer<QQmlContextData>, 4> oldLevelContexts =
402 levelContexts(object, outerContext);
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 });
415 for (QQmlRefPointer<QQmlContextData> ctx(ddata->context); ctx; ctx = ctx->linkedContext()) {
416 if (ctx->contextObject() == object)
417 ctx->setContextObject(
nullptr);
422 QObjectPrivate *objectPrivate = QObjectPrivate::get(object);
423 delete std::exchange(objectPrivate->metaObject,
nullptr);
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);
435 if (i >= oldLevelContexts.size() || i != replacedLevels)
437 const QQmlRefPointer<QQmlContextData> &oldLevelContext = oldLevelContexts[i];
438 const QQmlRefPointer<QV4::ExecutableCompilationUnit> oldLevelCu =
439 oldLevelContext->typeCompilationUnit();
440 if ((oldLevelCu == oldUnit ? newUnit : oldLevelCu) != level.newCu)
442 replacedContexts->insert(oldLevelContext.data(), { oldLevelContext, levelContext });
446 if (outerContext->contextObject() == object) {
447 ddata->ownContext = outerContext;
448 ddata->context = outerContext.data();
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);
458 ddata->context = outerContext.data();
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);
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);
486 for (
auto it = levels.crbegin(), end = levels.crend(); it != end && !ddata->isQueuedForDeletion;
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);
493 QQmlInstantiationInterrupt interrupt;
494 creator.finalize(interrupt);
499 if (!ddata->isQueuedForDeletion) {
501 QQmlObjectCreator creator(instanceLevel.context, instanceLevel.newCu, outerContext, QString(),
503 creator.repopulateBindings(instanceLevel.objectIndex, object, outerContext,
504 QQmlObjectCreator::InitFlag::None);
505 QQmlInstantiationInterrupt interrupt;
506 creator.finalize(interrupt);
510 patchCtx.refreshObjects();
511 patchCtx.restoreExternalState();
538 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
539 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
541 using ChangeType = QV4::CompiledData::ChangeType;
542 switch (change.type) {
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:
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:
584 case ChangeType::FunctionChanged:
590 case ChangeType::BindingChanged: {
591 Q_ASSERT(change.objectIndex >= 0);
592 Q_ASSERT(change.objectIndex < oldUnit->objectCount());
593 Q_ASSERT(change.objectIndex < newUnit->objectCount());
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;
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;
603 const BindingKind oldKind = bindingKind(oldBinding);
604 const BindingKind newKind = bindingKind(newBinding);
612 if (oldKind != newKind)
620 return BindingPatchContext::targetPropertyName(oldUnit, change.objectIndex, oldBinding)
621 == BindingPatchContext::targetPropertyName(newUnit, change.objectIndex, newBinding);
686 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
687 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
689 for (QObject *object : objects) {
690 QQmlData *ddata = QQmlData::get(object);
694 if (QQmlComponent *component = qobject_cast<QQmlComponent *>(object)) {
695 QQmlComponentPrivate *cp = QQmlComponentPrivate::get(component);
696 if (cp->compilationUnit() == oldUnit)
697 cp->setCompilationUnit(newUnit);
703 if (ddata->compilationUnit == oldUnit && ddata->cuObjectIndex < newUnit->objectCount())
704 ddata->compilationUnit = newUnit;
706 if (!ddata->hasVMEMetaObject)
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)
715 if (vmeMeta->qmlObjectId() < newUnit->objectCount()) {
716 vmeMeta->setCompilationUnit(newUnit);
720 const QQmlPropertyCache::ConstPtr newCache =
721 newUnit->propertyCachesPtr()->at(vmeMeta->qmlObjectId());
723 vmeMeta->setPropertyCache(newCache);
724 if (vmeMeta == mainVme)
725 ddata->propertyCache = newCache;
730 vmeMeta->setCompilationUnit(
nullptr);
831 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
833 QV4::ExecutionEngine *v4 = newUnit->engine;
834 QV4::Scope scope(v4);
835 QV4::ScopedValue function(scope);
837 for (QObject *object : objects) {
838 QQmlData *ddata = QQmlData::get(object);
839 if (!ddata || !ddata->hasVMEMetaObject)
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();
848 const QQmlRefPointer<QQmlContextData> context = vme->contextData();
852 const int objectIndex = vme->qmlObjectId();
853 if (objectIndex < 0 || objectIndex >= cu->objectCount())
856 const QV4::CompiledData::Object *obj = cu->objectAt(objectIndex);
857 const QQmlPropertyCache::ConstPtr cache = cu->propertyCachesPtr()->at(objectIndex);
861 QV4::Scoped<QV4::QmlContext> qmlContext(
862 scope, QV4::QmlContext::create(v4->rootContext(), context, object));
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();
869 const QQmlPropertyData *property = cache->property(name, object, context);
870 if (!property || !property->isVMEFunction())
874 function = runtimeFunction->isGenerator()
875 ? QV4::GeneratorFunction::create(qmlContext, runtimeFunction)
876 : QV4::FunctionObject::createScriptFunction(qmlContext, runtimeFunction);
877 mainVme->setVmeMethod(property->coreIndex(), function);
884 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
885 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit)
888 if (!newUnit->runtimeStrings)
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);
927 std::vector<RebuildTarget> rebuild;
928 QSet<QObject *> rebuildSet;
929 bool redirectedTypes =
false;
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))
936 rebuildSet.insert(object);
937 rebuild.push_back({ object, index, oldCu, newCu });
940 for (QObject *object : objects) {
941 const QVarLengthArray<
int, 4> indices = objectIndices(object, oldUnit);
942 for (
int index : indices) {
947 if (index >= oldUnit->objectCount() || !isComponentRoot(oldUnit, index))
956 if (!canRebuildComponentRootInPlace(oldUnit, newUnit, index)) {
957 QObject *outer = outerRebuildTarget(object, oldUnit, newUnit);
959 return PatchResult::Failed;
964 if (!redirectedTypes) {
965 redirectResolvedTypeReferences(newUnit->engine, oldUnit->baseCompilationUnit(),
966 newUnit->baseCompilationUnit());
967 redirectedTypes =
true;
973 const QQmlRefPointer<QV4::ExecutableCompilationUnit> outerOld =
974 QQmlData::get(outer)->compilationUnit;
975 const QQmlRefPointer<QV4::ExecutableCompilationUnit> outerNew =
976 outerOld->baseCompilationUnit() == oldUnit->baseCompilationUnit()
979 addRebuild(outer, QQmlData::get(outer)->cuObjectIndex, outerOld, outerNew);
983 addRebuild(object, indices.first(), oldUnit, newUnit);
992 std::sort(rebuild.begin(), rebuild.end(),
993 [](
const RebuildTarget &a,
const RebuildTarget &b) {
return a.index < b.index; });
998 QSet<QObject *> skip;
999 for (
const RebuildTarget &target : rebuild) {
1000 const QObjectList &children = target.object->children();
1001 for (QObject *child : children)
1005 ReplacedContexts replacedContexts;
1006 for (
const RebuildTarget &target : rebuild) {
1007 if (skip.contains(target.object))
1010 rebuildObject(target.object, target.index, target.oldCu, target.newCu, &replacedContexts);
1016 for (QObject *object : objects) {
1017 const QQmlData *ddata = QQmlData::get(object);
1018 if (ddata->compilationUnit != oldUnit || ddata->isQueuedForDeletion)
1020 BindingPatchContext::clearBindingsRecursive(object);
1021 BindingPatchContext::retireObject(object);