void StubViewTree::mutate()

in ReactCommon/react/renderer/mounting/StubViewTree.cpp [41:254]


void StubViewTree::mutate(ShadowViewMutationList const &mutations) {
  STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Mutating Begin"; });
  for (auto const &mutation : mutations) {
    switch (mutation.type) {
      case ShadowViewMutation::Create: {
        react_native_assert(mutation.parentShadowView == ShadowView{});
        react_native_assert(mutation.oldChildShadowView == ShadowView{});
        react_native_assert(mutation.newChildShadowView.props);
        auto stubView = std::make_shared<StubView>();
        stubView->update(mutation.newChildShadowView);
        auto tag = mutation.newChildShadowView.tag;
        STUB_VIEW_LOG({
          LOG(ERROR) << "StubView: Create [" << tag << "] ##"
                     << std::hash<ShadowView>{}((ShadowView)*stubView);
        });
        react_native_assert(registry.find(tag) == registry.end());
        registry[tag] = stubView;
        break;
      }

      case ShadowViewMutation::Delete: {
        STUB_VIEW_LOG({
          LOG(ERROR) << "StubView: Delete [" << mutation.oldChildShadowView.tag
                     << "] ##"
                     << std::hash<ShadowView>{}(mutation.oldChildShadowView);
        });
        react_native_assert(mutation.parentShadowView == ShadowView{});
        react_native_assert(mutation.newChildShadowView == ShadowView{});
        auto tag = mutation.oldChildShadowView.tag;
        react_native_assert(registry.find(tag) != registry.end());
        auto stubView = registry[tag];
        if ((ShadowView)(*stubView) != mutation.oldChildShadowView) {
          LOG(ERROR)
              << "StubView: ASSERT FAILURE: DELETE mutation assertion failure: oldChildShadowView does not match stubView: ["
              << mutation.oldChildShadowView.tag << "] stub hash: ##"
              << std::hash<ShadowView>{}((ShadowView)*stubView)
              << " old mutation hash: ##"
              << std::hash<ShadowView>{}(mutation.oldChildShadowView);
#ifdef RN_DEBUG_STRING_CONVERTIBLE
          LOG(ERROR) << "StubView: "
                     << getDebugPropsDescription((ShadowView)*stubView, {});
          LOG(ERROR) << "OldChildShadowView: "
                     << getDebugPropsDescription(
                            mutation.oldChildShadowView, {});
#endif
        }
        react_native_assert(
            (ShadowView)(*stubView) == mutation.oldChildShadowView);
        registry.erase(tag);
        break;
      }

      case ShadowViewMutation::Insert: {
        if (!mutation.mutatedViewIsVirtual()) {
          react_native_assert(mutation.oldChildShadowView == ShadowView{});
          auto parentTag = mutation.parentShadowView.tag;
          auto childTag = mutation.newChildShadowView.tag;
          if (registry.find(parentTag) == registry.end()) {
            LOG(ERROR)
                << "StubView: ASSERT FAILURE: INSERT mutation assertion failure: parentTag not found: ["
                << parentTag << "] inserting child: [" << childTag << "]";
          }
          if (registry.find(childTag) == registry.end()) {
            LOG(ERROR)
                << "StubView: ASSERT FAILURE: INSERT mutation assertion failure: childTag not found: ["
                << parentTag << "] inserting child: [" << childTag << "]";
          }
          react_native_assert(registry.find(parentTag) != registry.end());
          auto parentStubView = registry[parentTag];
          react_native_assert(registry.find(childTag) != registry.end());
          auto childStubView = registry[childTag];
          childStubView->update(mutation.newChildShadowView);
          STUB_VIEW_LOG({
            LOG(ERROR) << "StubView: Insert [" << childTag << "] into ["
                       << parentTag << "] @" << mutation.index << "("
                       << parentStubView->children.size() << " children)";
          });
          react_native_assert(childStubView->parentTag == NO_VIEW_TAG);
          react_native_assert(
              mutation.index >= 0 &&
              parentStubView->children.size() >=
                  static_cast<size_t>(mutation.index));
          childStubView->parentTag = parentTag;
          parentStubView->children.insert(
              parentStubView->children.begin() + mutation.index, childStubView);
        } else {
          auto childTag = mutation.newChildShadowView.tag;
          react_native_assert(registry.find(childTag) != registry.end());
          auto childStubView = registry[childTag];
          childStubView->update(mutation.newChildShadowView);
        }
        break;
      }

      case ShadowViewMutation::Remove: {
        if (!mutation.mutatedViewIsVirtual()) {
          react_native_assert(mutation.newChildShadowView == ShadowView{});
          auto parentTag = mutation.parentShadowView.tag;
          auto childTag = mutation.oldChildShadowView.tag;
          if (registry.find(parentTag) == registry.end()) {
            LOG(ERROR)
                << "StubView: ASSERT FAILURE: REMOVE mutation assertion failure: parentTag not found: ["
                << parentTag << "] removing child: [" << childTag << "]";
          }
          react_native_assert(registry.find(parentTag) != registry.end());
          auto parentStubView = registry[parentTag];
          STUB_VIEW_LOG({
            LOG(ERROR) << "StubView: Remove [" << childTag << "] from ["
                       << parentTag << "] @" << mutation.index << " with "
                       << parentStubView->children.size() << " children";
          });
          react_native_assert(
              mutation.index >= 0 &&
              parentStubView->children.size() >
                  static_cast<size_t>(mutation.index));
          react_native_assert(registry.find(childTag) != registry.end());
          auto childStubView = registry[childTag];
          if ((ShadowView)(*childStubView) != mutation.oldChildShadowView) {
            LOG(ERROR)
                << "StubView: ASSERT FAILURE: REMOVE mutation assertion failure: oldChildShadowView does not match oldStubView: ["
                << mutation.oldChildShadowView.tag << "] stub hash: ##"
                << std::hash<ShadowView>{}((ShadowView)*childStubView)
                << " old mutation hash: ##"
                << std::hash<ShadowView>{}(mutation.oldChildShadowView);
#ifdef RN_DEBUG_STRING_CONVERTIBLE
            LOG(ERROR) << "ChildStubView: "
                       << getDebugPropsDescription(
                              (ShadowView)*childStubView, {});
            LOG(ERROR) << "OldChildShadowView: "
                       << getDebugPropsDescription(
                              mutation.oldChildShadowView, {});
#endif
          }
          react_native_assert(
              (ShadowView)(*childStubView) == mutation.oldChildShadowView);
          react_native_assert(childStubView->parentTag == parentTag);
          STUB_VIEW_LOG({
            std::string strChildList = "";
            int i = 0;
            for (auto const &child : parentStubView->children) {
              strChildList.append(std::to_string(i));
              strChildList.append(":");
              strChildList.append(std::to_string(child->tag));
              strChildList.append(", ");
              i++;
            }
            LOG(ERROR) << "StubView: BEFORE REMOVE: Children of " << parentTag
                       << ": " << strChildList;
          });
          react_native_assert(
              mutation.index >= 0 &&
              parentStubView->children.size() >
                  static_cast<size_t>(mutation.index) &&
              parentStubView->children[mutation.index]->tag ==
                  childStubView->tag);
          childStubView->parentTag = NO_VIEW_TAG;
          parentStubView->children.erase(
              parentStubView->children.begin() + mutation.index);
        }
        break;
      }

      case ShadowViewMutation::Update: {
        STUB_VIEW_LOG({
          LOG(ERROR) << "StubView: Update [" << mutation.newChildShadowView.tag
                     << "] old hash: ##"
                     << std::hash<ShadowView>{}(mutation.oldChildShadowView)
                     << " new hash: ##"
                     << std::hash<ShadowView>{}(mutation.newChildShadowView);
        });
        react_native_assert(mutation.oldChildShadowView.tag != 0);
        react_native_assert(mutation.newChildShadowView.tag != 0);
        react_native_assert(mutation.newChildShadowView.props);
        react_native_assert(
            mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag);
        react_native_assert(
            registry.find(mutation.newChildShadowView.tag) != registry.end());
        auto oldStubView = registry[mutation.newChildShadowView.tag];
        react_native_assert(oldStubView->tag != 0);
        if ((ShadowView)(*oldStubView) != mutation.oldChildShadowView) {
          LOG(ERROR)
              << "StubView: ASSERT FAILURE: UPDATE mutation assertion failure: oldChildShadowView does not match oldStubView: ["
              << mutation.oldChildShadowView.tag << "] old stub hash: ##"
              << std::hash<ShadowView>{}((ShadowView)*oldStubView)
              << " old mutation hash: ##"
              << std::hash<ShadowView>{}(mutation.oldChildShadowView);
#ifdef RN_DEBUG_STRING_CONVERTIBLE
          LOG(ERROR) << "OldStubView: "
                     << getDebugPropsDescription((ShadowView)*oldStubView, {});
          LOG(ERROR) << "OldChildShadowView: "
                     << getDebugPropsDescription(
                            mutation.oldChildShadowView, {});
#endif
        }
        react_native_assert(
            (ShadowView)(*oldStubView) == mutation.oldChildShadowView);
        oldStubView->update(mutation.newChildShadowView);

        // Hash for stub view and the ShadowView should be identical - this
        // tests that StubView and ShadowView hash are equivalent.
        react_native_assert(
            std::hash<ShadowView>{}((ShadowView)*oldStubView) ==
            std::hash<ShadowView>{}(mutation.newChildShadowView));

        break;
      }
    }
  }
  STUB_VIEW_LOG({ LOG(ERROR) << "StubView: Mutating End"; });

  // For iOS especially: flush logs because some might be lost on iOS if an
  // assert is hit right after this.
  google::FlushLogFiles(google::GLOG_INFO);
}