diff options
author | Bram Moolenaar <Bram@vim.org> | 2021-12-14 18:14:37 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-12-14 18:14:37 +0000 |
commit | 7509ad8b0fad56f88288977decbeca3640406c82 (patch) | |
tree | bd5e3baad50de512990bb3ba1257452d4bf5f244 /src/vim9execute.c | |
parent | 919c12c19a9785b856bc32dfe6cd8e9277178b62 (diff) | |
download | vim-git-7509ad8b0fad56f88288977decbeca3640406c82.tar.gz |
patch 8.2.3809: Vim9: crash when garbage collecting a nested partialv8.2.3809
Problem: Vim9: crash when garbage collecting a nested partial. (Virginia
Senioria)
Solution: Set references in all the funcstacks. (closes #9348)
Diffstat (limited to 'src/vim9execute.c')
-rw-r--r-- | src/vim9execute.c | 52 |
1 files changed, 51 insertions, 1 deletions
diff --git a/src/vim9execute.c b/src/vim9execute.c index 3160f0df2..53beee103 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -468,6 +468,31 @@ call_dfunc( // Get pointer to item in the stack. #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx) +// Double linked list of funcstack_T in use. +static funcstack_T *first_funcstack = NULL; + + static void +add_funcstack_to_list(funcstack_T *funcstack) +{ + // Link in list of funcstacks. + if (first_funcstack != NULL) + first_funcstack->fs_prev = funcstack; + funcstack->fs_next = first_funcstack; + funcstack->fs_prev = NULL; + first_funcstack = funcstack; +} + + static void +remove_funcstack_from_list(funcstack_T *funcstack) +{ + if (funcstack->fs_prev == NULL) + first_funcstack = funcstack->fs_next; + else + funcstack->fs_prev->fs_next = funcstack->fs_next; + if (funcstack->fs_next != NULL) + funcstack->fs_next->fs_prev = funcstack->fs_prev; +} + /* * Used when returning from a function: Check if any closure is still * referenced. If so then move the arguments and variables to a separate piece @@ -540,6 +565,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments) // Move them to the called function. if (funcstack == NULL) return FAIL; + funcstack->fs_var_offset = argcount + STACK_FRAME_SIZE; funcstack->fs_ga.ga_len = funcstack->fs_var_offset + dfunc->df_varcount; stack = ALLOC_CLEAR_MULT(typval_T, funcstack->fs_ga.ga_len); @@ -549,6 +575,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments) vim_free(funcstack); return FAIL; } + add_funcstack_to_list(funcstack); // Move or copy the arguments. for (idx = 0; idx < argcount; ++idx) @@ -571,7 +598,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments) // local variable, has a reference count for the variable, thus // will never go down to zero. When all these refcounts are one // then the funcstack is unused. We need to count how many we have - // so we need when to check. + // so we know when to check. if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL) { int i; @@ -643,11 +670,34 @@ funcstack_check_refcount(funcstack_T *funcstack) for (i = 0; i < gap->ga_len; ++i) clear_tv(stack + i); vim_free(stack); + remove_funcstack_from_list(funcstack); vim_free(funcstack); } } /* + * For garbage collecting: set references in all variables referenced by + * all funcstacks. + */ + int +set_ref_in_funcstacks(int copyID) +{ + funcstack_T *funcstack; + + for (funcstack = first_funcstack; funcstack != NULL; + funcstack = funcstack->fs_next) + { + typval_T *stack = funcstack->fs_ga.ga_data; + int i; + + for (i = 0; i < funcstack->fs_ga.ga_len; ++i) + if (set_ref_in_item(stack + i, copyID, NULL, NULL)) + return TRUE; // abort + } + return FALSE; +} + +/* * Return from the current function. */ static int |