[Bf-blender-cvs] [2cc5af9c553] master: Fix T86431: Keep memory location of the window manager on file load

Campbell Barton noreply at git.blender.org
Thu Mar 11 14:51:41 CET 2021


Commit: 2cc5af9c553cfc00b7d4616445ad954597a92d94
Author: Campbell Barton
Date:   Fri Mar 12 00:34:23 2021 +1100
Branches: master
https://developer.blender.org/rB2cc5af9c553cfc00b7d4616445ad954597a92d94

Fix T86431: Keep memory location of the window manager on file load

Keep the pointer location from the initial window-manager
between file load operations.

This is needed as the Python API may hold references to keymaps for e.g.
which are transferred to the newly loaded window manager,
without their `PointerRNA.owner_id` fields being updated.

Since there is only ever one window manager, keep the memory at the same location so the Python ID pointers stay valid.

Reviewed By: mont29

Ref D10690

===================================================================

M	source/blender/blenkernel/BKE_lib_remap.h
M	source/blender/blenkernel/intern/lib_remap.c
M	source/blender/windowmanager/intern/wm_files.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index a8d75213d39..6e81273b82b 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -76,6 +76,8 @@ enum {
   ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4,
   /** Do not remap library override pointers. */
   ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5,
+  /** Don't touch the user count (use for low level actions such as swapping pointers). */
+  ID_REMAP_SKIP_USER_CLEAR = 1 << 6,
 };
 
 /* Note: Requiring new_id to be non-null, this *may* not be the case ultimately,
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 56f7bb0be6f..0218cb913a8 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -422,15 +422,17 @@ static void libblock_remap_data(
     FOREACH_MAIN_ID_END;
   }
 
-  /* XXX We may not want to always 'transfer' fake-user from old to new id...
-   *     Think for now it's desired behavior though,
-   *     we can always add an option (flag) to control this later if needed. */
-  if (old_id && (old_id->flag & LIB_FAKEUSER)) {
-    id_fake_user_clear(old_id);
-    id_fake_user_set(new_id);
-  }
+  if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
+    /* XXX We may not want to always 'transfer' fake-user from old to new id...
+     *     Think for now it's desired behavior though,
+     *     we can always add an option (flag) to control this later if needed. */
+    if (old_id && (old_id->flag & LIB_FAKEUSER)) {
+      id_fake_user_clear(old_id);
+      id_fake_user_set(new_id);
+    }
 
-  id_us_clear_real(old_id);
+    id_us_clear_real(old_id);
+  }
 
   if (new_id && (new_id->tag & LIB_TAG_INDIRECT) &&
       (r_id_remap_data->status & ID_REMAP_IS_LINKED_DIRECT)) {
@@ -479,12 +481,14 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
   skipped_direct = id_remap_data.skipped_direct;
   skipped_refcounted = id_remap_data.skipped_refcounted;
 
-  /* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
-   * count has actually been incremented for that, we have to decrease once more its user count...
-   * unless we had to skip some 'user_one' cases. */
-  if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
-      !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
-    id_us_clear_real(old_id);
+  if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
+    /* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
+     * count has actually been incremented for that, we have to decrease once more its user
+     * count... unless we had to skip some 'user_one' cases. */
+    if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
+        !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
+      id_us_clear_real(old_id);
+    }
   }
 
   if (old_id->us - skipped_refcounted < 0) {
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 2d1342da2fb..bd220e2ff95 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -81,6 +81,7 @@
 #include "BKE_idprop.h"
 #include "BKE_lib_id.h"
 #include "BKE_lib_override.h"
+#include "BKE_lib_remap.h"
 #include "BKE_main.h"
 #include "BKE_packedFile.h"
 #include "BKE_report.h"
@@ -296,6 +297,29 @@ static void wm_window_match_replace_by_file_wm(bContext *C,
 {
   wmWindowManager *oldwm = current_wm_list->first;
   wmWindowManager *wm = readfile_wm_list->first; /* will become our new WM */
+
+  /* Support window-manager ID references being held between file load operations by keeping
+   * #Main.wm.first memory address in-place, while swapping all of it's contents.
+   *
+   * This is needed so items such as key-maps can be held by an add-on,
+   * without it pointing to invalid memory, see: T86431 */
+  {
+    /* Referencing the window-manager pointer from elsewhere in the file is highly unlikely
+     * however it's possible with ID-properties & animation-drivers.
+     * At some point we could check on disallowing this since it doesn't seem practical. */
+    Main *bmain = G_MAIN;
+    BLI_assert(bmain->relations == NULL);
+    BKE_libblock_remap(bmain, wm, oldwm, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_USER_CLEAR);
+
+    /* Simple pointer swapping step. */
+    BLI_remlink(current_wm_list, oldwm);
+    BLI_remlink(readfile_wm_list, wm);
+    SWAP(wmWindowManager, *oldwm, *wm);
+    SWAP(wmWindowManager *, oldwm, wm);
+    BLI_addhead(current_wm_list, oldwm);
+    BLI_addhead(readfile_wm_list, wm);
+  }
+
   bool has_match = false;
 
   /* this code could move to setup_appdata */



More information about the Bf-blender-cvs mailing list