diff options
author | Bram Moolenaar <Bram@vim.org> | 2023-06-05 16:53:25 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2023-06-05 16:53:25 +0100 |
commit | f7ca56f7193f8b383be43f1f6b3a6c6ca77b4233 (patch) | |
tree | ce30cd88fa64abf5b4a73366dd4a463f0d141ce8 /src | |
parent | 5c606846b9a43c7e6b94c7e96838f7532792f557 (diff) |
patch 9.0.1609: crash when an object indirectly references itselfv9.0.1609
Problem: Crash when an object indirectly references itself.
Solution: Avoid clearing an object while it is already being cleared.
(closes #12494)
Diffstat (limited to 'src')
-rw-r--r-- | src/testdir/test_vim9_class.vim | 27 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9class.c | 15 |
3 files changed, 41 insertions, 3 deletions
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 52812eac73..1d0d77b298 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -925,6 +925,33 @@ func Test_class_garbagecollect() echo Point.pl Point.pd END call v9.CheckScriptSuccess(lines) + + let lines =<< trim END + vim9script + + interface View + endinterface + + class Widget + this.view: View + endclass + + class MyView implements View + this.widget: Widget + + def new() + # this will result in a circular reference to this object + this.widget = Widget.new(this) + enddef + endclass + + var view = MyView.new() + + # overwrite "view", will be garbage-collected next + view = MyView.new() + test_garbagecollect_now() + END + call v9.CheckScriptSuccess(lines) endfunc def Test_class_function() diff --git a/src/version.c b/src/version.c index 1a8dd0e604..1288490670 100644 --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1609, +/**/ 1608, /**/ 1607, diff --git a/src/vim9class.c b/src/vim9class.c index 734967a802..12b09677b0 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -1497,6 +1497,9 @@ copy_object(typval_T *from, typval_T *to) static void object_clear(object_T *obj) { + // Avoid a recursive call, it can happen if "obj" has a circular reference. + obj->obj_refcount = INT_MAX; + class_T *cl = obj->obj_class; // the member values are just after the object structure @@ -1619,6 +1622,8 @@ object_created(object_T *obj) first_object = obj; } +static object_T *next_nonref_obj = NULL; + /* * Call this function when an object has been cleared and is about to be freed. * It is removed from the list headed by "first_object". @@ -1632,6 +1637,10 @@ object_cleared(object_T *obj) obj->obj_prev_used->obj_next_used = obj->obj_next_used; else if (first_object == obj) first_object = obj->obj_next_used; + + // update the next object to check if needed + if (obj == next_nonref_obj) + next_nonref_obj = obj->obj_next_used; } /* @@ -1641,11 +1650,10 @@ object_cleared(object_T *obj) object_free_nonref(int copyID) { int did_free = FALSE; - object_T *next_obj; - for (object_T *obj = first_object; obj != NULL; obj = next_obj) + for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj) { - next_obj = obj->obj_next_used; + next_nonref_obj = obj->obj_next_used; if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { // Free the object and items it contains. @@ -1654,6 +1662,7 @@ object_free_nonref(int copyID) } } + next_nonref_obj = NULL; return did_free; } |