comparison src/vim9execute.c @ 26560:454a1c9ef797 v8.2.3809

patch 8.2.3809: Vim9: crash when garbage collecting a nested partial Commit: https://github.com/vim/vim/commit/7509ad8b0fad56f88288977decbeca3640406c82 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Dec 14 18:14:37 2021 +0000 patch 8.2.3809: Vim9: crash when garbage collecting a nested partial Problem: Vim9: crash when garbage collecting a nested partial. (Virginia Senioria) Solution: Set references in all the funcstacks. (closes #9348)
author Bram Moolenaar <Bram@vim.org>
date Tue, 14 Dec 2021 19:15:04 +0100
parents ad00a5cb005d
children fac6673086df
comparison
equal deleted inserted replaced
26559:177872ca0db9 26560:454a1c9ef797
466 } 466 }
467 467
468 // Get pointer to item in the stack. 468 // Get pointer to item in the stack.
469 #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx) 469 #define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
470 470
471 // Double linked list of funcstack_T in use.
472 static funcstack_T *first_funcstack = NULL;
473
474 static void
475 add_funcstack_to_list(funcstack_T *funcstack)
476 {
477 // Link in list of funcstacks.
478 if (first_funcstack != NULL)
479 first_funcstack->fs_prev = funcstack;
480 funcstack->fs_next = first_funcstack;
481 funcstack->fs_prev = NULL;
482 first_funcstack = funcstack;
483 }
484
485 static void
486 remove_funcstack_from_list(funcstack_T *funcstack)
487 {
488 if (funcstack->fs_prev == NULL)
489 first_funcstack = funcstack->fs_next;
490 else
491 funcstack->fs_prev->fs_next = funcstack->fs_next;
492 if (funcstack->fs_next != NULL)
493 funcstack->fs_next->fs_prev = funcstack->fs_prev;
494 }
495
471 /* 496 /*
472 * Used when returning from a function: Check if any closure is still 497 * Used when returning from a function: Check if any closure is still
473 * referenced. If so then move the arguments and variables to a separate piece 498 * referenced. If so then move the arguments and variables to a separate piece
474 * of stack to be used when the closure is called. 499 * of stack to be used when the closure is called.
475 * When "free_arguments" is TRUE the arguments are to be freed. 500 * When "free_arguments" is TRUE the arguments are to be freed.
538 563
539 // A closure is using the arguments and/or local variables. 564 // A closure is using the arguments and/or local variables.
540 // Move them to the called function. 565 // Move them to the called function.
541 if (funcstack == NULL) 566 if (funcstack == NULL)
542 return FAIL; 567 return FAIL;
568
543 funcstack->fs_var_offset = argcount + STACK_FRAME_SIZE; 569 funcstack->fs_var_offset = argcount + STACK_FRAME_SIZE;
544 funcstack->fs_ga.ga_len = funcstack->fs_var_offset + dfunc->df_varcount; 570 funcstack->fs_ga.ga_len = funcstack->fs_var_offset + dfunc->df_varcount;
545 stack = ALLOC_CLEAR_MULT(typval_T, funcstack->fs_ga.ga_len); 571 stack = ALLOC_CLEAR_MULT(typval_T, funcstack->fs_ga.ga_len);
546 funcstack->fs_ga.ga_data = stack; 572 funcstack->fs_ga.ga_data = stack;
547 if (stack == NULL) 573 if (stack == NULL)
548 { 574 {
549 vim_free(funcstack); 575 vim_free(funcstack);
550 return FAIL; 576 return FAIL;
551 } 577 }
578 add_funcstack_to_list(funcstack);
552 579
553 // Move or copy the arguments. 580 // Move or copy the arguments.
554 for (idx = 0; idx < argcount; ++idx) 581 for (idx = 0; idx < argcount; ++idx)
555 { 582 {
556 tv = STACK_TV(top + idx); 583 tv = STACK_TV(top + idx);
569 596
570 // A partial created for a local function, that is also used as a 597 // A partial created for a local function, that is also used as a
571 // local variable, has a reference count for the variable, thus 598 // local variable, has a reference count for the variable, thus
572 // will never go down to zero. When all these refcounts are one 599 // will never go down to zero. When all these refcounts are one
573 // then the funcstack is unused. We need to count how many we have 600 // then the funcstack is unused. We need to count how many we have
574 // so we need when to check. 601 // so we know when to check.
575 if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL) 602 if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL)
576 { 603 {
577 int i; 604 int i;
578 605
579 for (i = 0; i < closure_count; ++i) 606 for (i = 0; i < closure_count; ++i)
641 // All partials referencing the funcstack have a reference count of 668 // All partials referencing the funcstack have a reference count of
642 // one, thus the funcstack is no longer of use. 669 // one, thus the funcstack is no longer of use.
643 for (i = 0; i < gap->ga_len; ++i) 670 for (i = 0; i < gap->ga_len; ++i)
644 clear_tv(stack + i); 671 clear_tv(stack + i);
645 vim_free(stack); 672 vim_free(stack);
673 remove_funcstack_from_list(funcstack);
646 vim_free(funcstack); 674 vim_free(funcstack);
647 } 675 }
676 }
677
678 /*
679 * For garbage collecting: set references in all variables referenced by
680 * all funcstacks.
681 */
682 int
683 set_ref_in_funcstacks(int copyID)
684 {
685 funcstack_T *funcstack;
686
687 for (funcstack = first_funcstack; funcstack != NULL;
688 funcstack = funcstack->fs_next)
689 {
690 typval_T *stack = funcstack->fs_ga.ga_data;
691 int i;
692
693 for (i = 0; i < funcstack->fs_ga.ga_len; ++i)
694 if (set_ref_in_item(stack + i, copyID, NULL, NULL))
695 return TRUE; // abort
696 }
697 return FALSE;
648 } 698 }
649 699
650 /* 700 /*
651 * Return from the current function. 701 * Return from the current function.
652 */ 702 */