Mercurial > vim
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 */ |