Mercurial > vim
comparison src/userfunc.c @ 9735:8037eb704e93 v7.4.2143
commit https://github.com/vim/vim/commit/bc7ce675b2d1c9fb58c067eff3edd59abc30aba4
Author: Bram Moolenaar <Bram@vim.org>
Date: Mon Aug 1 22:49:22 2016 +0200
patch 7.4.2143
Problem: A funccal is garbage collected while it can still be used.
Solution: Set copyID in all referenced functions. Do not list lambda
functions with ":function".
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Mon, 01 Aug 2016 23:00:07 +0200 |
parents | 59565cdd7261 |
children | 9ce5941b77d3 |
comparison
equal
deleted
inserted
replaced
9734:9bdde041199c | 9735:8037eb704e93 |
---|---|
63 # ifdef __BORLANDC__ | 63 # ifdef __BORLANDC__ |
64 _RTLENTRYF | 64 _RTLENTRYF |
65 # endif | 65 # endif |
66 prof_self_cmp(const void *s1, const void *s2); | 66 prof_self_cmp(const void *s1, const void *s2); |
67 #endif | 67 #endif |
68 static void funccal_unref(funccall_T *fc, ufunc_T *fp); | 68 static void funccal_unref(funccall_T *fc, ufunc_T *fp, int force); |
69 | 69 |
70 void | 70 void |
71 func_init() | 71 func_init() |
72 { | 72 { |
73 hash_init(&func_hashtab); | 73 hash_init(&func_hashtab); |
180 register_closure(ufunc_T *fp) | 180 register_closure(ufunc_T *fp) |
181 { | 181 { |
182 if (fp->uf_scoped == current_funccal) | 182 if (fp->uf_scoped == current_funccal) |
183 /* no change */ | 183 /* no change */ |
184 return OK; | 184 return OK; |
185 funccal_unref(fp->uf_scoped, fp); | 185 funccal_unref(fp->uf_scoped, fp, FALSE); |
186 fp->uf_scoped = current_funccal; | 186 fp->uf_scoped = current_funccal; |
187 current_funccal->fc_refcount++; | 187 current_funccal->fc_refcount++; |
188 func_ptr_ref(current_funccal->func); | |
189 | 188 |
190 if (ga_grow(¤t_funccal->fc_funcs, 1) == FAIL) | 189 if (ga_grow(¤t_funccal->fc_funcs, 1) == FAIL) |
191 return FAIL; | 190 return FAIL; |
192 ((ufunc_T **)current_funccal->fc_funcs.ga_data) | 191 ((ufunc_T **)current_funccal->fc_funcs.ga_data) |
193 [current_funccal->fc_funcs.ga_len++] = fp; | 192 [current_funccal->fc_funcs.ga_len++] = fp; |
601 | 600 |
602 for (i = 0; i < fc->fc_funcs.ga_len; ++i) | 601 for (i = 0; i < fc->fc_funcs.ga_len; ++i) |
603 { | 602 { |
604 ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; | 603 ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; |
605 | 604 |
606 if (fp != NULL) | 605 /* When garbage collecting a funccall_T may be freed before the |
607 { | 606 * function that references it, clear its uf_scoped field. |
608 /* Function may have been redefined and point to another | 607 * The function may have been redefined and point to another |
609 * funccall_T, don't clear it then. */ | 608 * funccall_T, don't clear it then. */ |
610 if (fp->uf_scoped == fc) | 609 if (fp != NULL && fp->uf_scoped == fc) |
611 fp->uf_scoped = NULL; | 610 fp->uf_scoped = NULL; |
612 func_ptr_unref(fc->func); | |
613 } | |
614 } | 611 } |
615 ga_clear(&fc->fc_funcs); | 612 ga_clear(&fc->fc_funcs); |
616 | 613 |
617 /* The a: variables typevals may not have been allocated, only free the | 614 /* The a: variables typevals may not have been allocated, only free the |
618 * allocated variables. */ | 615 * allocated variables. */ |
1028 } | 1025 } |
1029 | 1026 |
1030 /* | 1027 /* |
1031 * Unreference "fc": decrement the reference count and free it when it | 1028 * Unreference "fc": decrement the reference count and free it when it |
1032 * becomes zero. "fp" is detached from "fc". | 1029 * becomes zero. "fp" is detached from "fc". |
1030 * When "force" is TRUE we are exiting. | |
1033 */ | 1031 */ |
1034 static void | 1032 static void |
1035 funccal_unref(funccall_T *fc, ufunc_T *fp) | 1033 funccal_unref(funccall_T *fc, ufunc_T *fp, int force) |
1036 { | 1034 { |
1037 funccall_T **pfc; | 1035 funccall_T **pfc; |
1038 int i; | 1036 int i; |
1039 | 1037 |
1040 if (fc == NULL) | 1038 if (fc == NULL) |
1041 return; | 1039 return; |
1042 | 1040 |
1043 if (--fc->fc_refcount <= 0 | 1041 if (--fc->fc_refcount <= 0 && (force || ( |
1044 && fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT | 1042 fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT |
1045 && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT | 1043 && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT |
1046 && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) | 1044 && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) |
1047 for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) | 1045 for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) |
1048 { | 1046 { |
1049 if (fc == *pfc) | 1047 if (fc == *pfc) |
1050 { | 1048 { |
1051 *pfc = fc->caller; | 1049 *pfc = fc->caller; |
1052 free_funccal(fc, TRUE); | 1050 free_funccal(fc, TRUE); |
1053 return; | 1051 return; |
1054 } | 1052 } |
1055 } | 1053 } |
1056 for (i = 0; i < fc->fc_funcs.ga_len; ++i) | 1054 for (i = 0; i < fc->fc_funcs.ga_len; ++i) |
1057 { | |
1058 if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) | 1055 if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) |
1059 { | |
1060 func_ptr_unref(fc->func); | |
1061 ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL; | 1056 ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL; |
1062 } | |
1063 } | |
1064 } | 1057 } |
1065 | 1058 |
1066 /* | 1059 /* |
1067 * Remove the function from the function hashtable. If the function was | 1060 * Remove the function from the function hashtable. If the function was |
1068 * deleted while it still has references this was already done. | 1061 * deleted while it still has references this was already done. |
1081 return FALSE; | 1074 return FALSE; |
1082 } | 1075 } |
1083 | 1076 |
1084 /* | 1077 /* |
1085 * Free a function and remove it from the list of functions. | 1078 * Free a function and remove it from the list of functions. |
1079 * When "force" is TRUE we are exiting. | |
1086 */ | 1080 */ |
1087 static void | 1081 static void |
1088 func_free(ufunc_T *fp) | 1082 func_free(ufunc_T *fp, int force) |
1089 { | 1083 { |
1090 /* clear this function */ | 1084 /* clear this function */ |
1091 ga_clear_strings(&(fp->uf_args)); | 1085 ga_clear_strings(&(fp->uf_args)); |
1092 ga_clear_strings(&(fp->uf_lines)); | 1086 ga_clear_strings(&(fp->uf_lines)); |
1093 #ifdef FEAT_PROFILE | 1087 #ifdef FEAT_PROFILE |
1098 /* only remove it when not done already, otherwise we would remove a newer | 1092 /* only remove it when not done already, otherwise we would remove a newer |
1099 * version of the function */ | 1093 * version of the function */ |
1100 if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) | 1094 if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) |
1101 func_remove(fp); | 1095 func_remove(fp); |
1102 | 1096 |
1103 funccal_unref(fp->uf_scoped, fp); | 1097 funccal_unref(fp->uf_scoped, fp, force); |
1104 | 1098 |
1105 vim_free(fp); | 1099 vim_free(fp); |
1106 } | 1100 } |
1107 | 1101 |
1108 #if defined(EXITFREE) || defined(PROTO) | 1102 #if defined(EXITFREE) || defined(PROTO) |
1115 * hash table. */ | 1109 * hash table. */ |
1116 while (func_hashtab.ht_used > 0) | 1110 while (func_hashtab.ht_used > 0) |
1117 for (hi = func_hashtab.ht_array; ; ++hi) | 1111 for (hi = func_hashtab.ht_array; ; ++hi) |
1118 if (!HASHITEM_EMPTY(hi)) | 1112 if (!HASHITEM_EMPTY(hi)) |
1119 { | 1113 { |
1120 func_free(HI2UF(hi)); | 1114 func_free(HI2UF(hi), TRUE); |
1121 break; | 1115 break; |
1122 } | 1116 } |
1123 hash_clear(&func_hashtab); | 1117 hash_clear(&func_hashtab); |
1124 } | 1118 } |
1125 #endif | 1119 #endif |
1329 firstline, lastline, | 1323 firstline, lastline, |
1330 (fp->uf_flags & FC_DICT) ? selfdict : NULL); | 1324 (fp->uf_flags & FC_DICT) ? selfdict : NULL); |
1331 if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) | 1325 if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) |
1332 /* Function was unreferenced while being used, free it | 1326 /* Function was unreferenced while being used, free it |
1333 * now. */ | 1327 * now. */ |
1334 func_free(fp); | 1328 func_free(fp, FALSE); |
1335 if (did_save_redo) | 1329 if (did_save_redo) |
1336 restoreRedobuff(); | 1330 restoreRedobuff(); |
1337 restore_search_patterns(); | 1331 restore_search_patterns(); |
1338 error = ERROR_NONE; | 1332 error = ERROR_NONE; |
1339 } | 1333 } |
1670 *pp = end; | 1664 *pp = end; |
1671 | 1665 |
1672 theend: | 1666 theend: |
1673 clear_lval(&lv); | 1667 clear_lval(&lv); |
1674 return name; | 1668 return name; |
1669 } | |
1670 | |
1671 /* | |
1672 * There are two kinds of function names: | |
1673 * 1. ordinary names, function defined with :function | |
1674 * 2. numbered functions and lambdas | |
1675 * For the first we only count the name stored in func_hashtab as a reference, | |
1676 * using function() does not count as a reference, because the function is | |
1677 * looked up by name. | |
1678 */ | |
1679 static int | |
1680 func_name_refcount(char_u *name) | |
1681 { | |
1682 return isdigit(*name) || *name == '<'; | |
1675 } | 1683 } |
1676 | 1684 |
1677 /* | 1685 /* |
1678 * ":function" | 1686 * ":function" |
1679 */ | 1687 */ |
1719 { | 1727 { |
1720 if (!HASHITEM_EMPTY(hi)) | 1728 if (!HASHITEM_EMPTY(hi)) |
1721 { | 1729 { |
1722 --todo; | 1730 --todo; |
1723 fp = HI2UF(hi); | 1731 fp = HI2UF(hi); |
1724 if (!isdigit(*fp->uf_name)) | 1732 if (!func_name_refcount(fp->uf_name)) |
1725 list_func_head(fp, FALSE); | 1733 list_func_head(fp, FALSE); |
1726 } | 1734 } |
1727 } | 1735 } |
1728 } | 1736 } |
1729 eap->nextcmd = check_nextcmd(eap->arg); | 1737 eap->nextcmd = check_nextcmd(eap->arg); |
2654 } | 2662 } |
2655 | 2663 |
2656 #endif /* FEAT_CMDL_COMPL */ | 2664 #endif /* FEAT_CMDL_COMPL */ |
2657 | 2665 |
2658 /* | 2666 /* |
2659 * There are two kinds of function names: | |
2660 * 1. ordinary names, function defined with :function | |
2661 * 2. numbered functions and lambdas | |
2662 * For the first we only count the name stored in func_hashtab as a reference, | |
2663 * using function() does not count as a reference, because the function is | |
2664 * looked up by name. | |
2665 */ | |
2666 static int | |
2667 func_name_refcount(char_u *name) | |
2668 { | |
2669 return isdigit(*name) || *name == '<'; | |
2670 } | |
2671 | |
2672 /* | |
2673 * ":delfunction {name}" | 2667 * ":delfunction {name}" |
2674 */ | 2668 */ |
2675 void | 2669 void |
2676 ex_delfunction(exarg_T *eap) | 2670 ex_delfunction(exarg_T *eap) |
2677 { | 2671 { |
2736 if (func_remove(fp)) | 2730 if (func_remove(fp)) |
2737 fp->uf_refcount--; | 2731 fp->uf_refcount--; |
2738 fp->uf_flags |= FC_DELETED; | 2732 fp->uf_flags |= FC_DELETED; |
2739 } | 2733 } |
2740 else | 2734 else |
2741 func_free(fp); | 2735 func_free(fp, FALSE); |
2742 } | 2736 } |
2743 } | 2737 } |
2744 } | 2738 } |
2745 | 2739 |
2746 /* | 2740 /* |
2765 if (fp != NULL && --fp->uf_refcount <= 0) | 2759 if (fp != NULL && --fp->uf_refcount <= 0) |
2766 { | 2760 { |
2767 /* Only delete it when it's not being used. Otherwise it's done | 2761 /* Only delete it when it's not being used. Otherwise it's done |
2768 * when "uf_calls" becomes zero. */ | 2762 * when "uf_calls" becomes zero. */ |
2769 if (fp->uf_calls == 0) | 2763 if (fp->uf_calls == 0) |
2770 func_free(fp); | 2764 func_free(fp, FALSE); |
2771 } | 2765 } |
2772 } | 2766 } |
2773 | 2767 |
2774 /* | 2768 /* |
2775 * Unreference a Function: decrement the reference count and free it when it | 2769 * Unreference a Function: decrement the reference count and free it when it |
2781 if (fp != NULL && --fp->uf_refcount <= 0) | 2775 if (fp != NULL && --fp->uf_refcount <= 0) |
2782 { | 2776 { |
2783 /* Only delete it when it's not being used. Otherwise it's done | 2777 /* Only delete it when it's not being used. Otherwise it's done |
2784 * when "uf_calls" becomes zero. */ | 2778 * when "uf_calls" becomes zero. */ |
2785 if (fp->uf_calls == 0) | 2779 if (fp->uf_calls == 0) |
2786 func_free(fp); | 2780 func_free(fp, FALSE); |
2787 } | 2781 } |
2788 } | 2782 } |
2789 | 2783 |
2790 /* | 2784 /* |
2791 * Count a reference to a Function. | 2785 * Count a reference to a Function. |
3674 NULL); | 3668 NULL); |
3675 } | 3669 } |
3676 return abort; | 3670 return abort; |
3677 } | 3671 } |
3678 | 3672 |
3679 /* | 3673 static int |
3680 * Set "copyID" in all local vars and arguments in the call stack. | 3674 set_ref_in_funccal(funccall_T *fc, int copyID) |
3681 */ | 3675 { |
3682 int | 3676 int abort = FALSE; |
3683 set_ref_in_call_stack(int copyID) | 3677 |
3684 { | 3678 if (fc->fc_copyID != copyID) |
3685 int abort = FALSE; | |
3686 funccall_T *fc; | |
3687 | |
3688 for (fc = current_funccal; fc != NULL; fc = fc->caller) | |
3689 { | 3679 { |
3690 fc->fc_copyID = copyID; | 3680 fc->fc_copyID = copyID; |
3691 abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); | 3681 abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); |
3692 abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); | 3682 abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); |
3683 abort = abort || set_ref_in_func(NULL, fc->func, copyID); | |
3684 } | |
3685 return abort; | |
3686 } | |
3687 | |
3688 /* | |
3689 * Set "copyID" in all local vars and arguments in the call stack. | |
3690 */ | |
3691 int | |
3692 set_ref_in_call_stack(int copyID) | |
3693 { | |
3694 int abort = FALSE; | |
3695 funccall_T *fc; | |
3696 | |
3697 for (fc = current_funccal; fc != NULL; fc = fc->caller) | |
3698 abort = abort || set_ref_in_funccal(fc, copyID); | |
3699 return abort; | |
3700 } | |
3701 | |
3702 /* | |
3703 * Set "copyID" in all functions available by name. | |
3704 */ | |
3705 int | |
3706 set_ref_in_functions(int copyID) | |
3707 { | |
3708 int todo; | |
3709 hashitem_T *hi = NULL; | |
3710 int abort = FALSE; | |
3711 ufunc_T *fp; | |
3712 | |
3713 todo = (int)func_hashtab.ht_used; | |
3714 for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) | |
3715 { | |
3716 if (!HASHITEM_EMPTY(hi)) | |
3717 { | |
3718 --todo; | |
3719 fp = HI2UF(hi); | |
3720 if (!func_name_refcount(fp->uf_name)) | |
3721 abort = abort || set_ref_in_func(NULL, fp, copyID); | |
3722 } | |
3693 } | 3723 } |
3694 return abort; | 3724 return abort; |
3695 } | 3725 } |
3696 | 3726 |
3697 /* | 3727 /* |
3709 return abort; | 3739 return abort; |
3710 } | 3740 } |
3711 | 3741 |
3712 /* | 3742 /* |
3713 * Mark all lists and dicts referenced through function "name" with "copyID". | 3743 * Mark all lists and dicts referenced through function "name" with "copyID". |
3714 * "list_stack" is used to add lists to be marked. Can be NULL. | |
3715 * "ht_stack" is used to add hashtabs to be marked. Can be NULL. | |
3716 * | |
3717 * Returns TRUE if setting references failed somehow. | 3744 * Returns TRUE if setting references failed somehow. |
3718 */ | 3745 */ |
3719 int | 3746 int |
3720 set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) | 3747 set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) |
3721 { | 3748 { |
3723 funccall_T *fc; | 3750 funccall_T *fc; |
3724 int error = ERROR_NONE; | 3751 int error = ERROR_NONE; |
3725 char_u fname_buf[FLEN_FIXED + 1]; | 3752 char_u fname_buf[FLEN_FIXED + 1]; |
3726 char_u *tofree = NULL; | 3753 char_u *tofree = NULL; |
3727 char_u *fname; | 3754 char_u *fname; |
3755 int abort = FALSE; | |
3728 | 3756 |
3729 if (name == NULL && fp_in == NULL) | 3757 if (name == NULL && fp_in == NULL) |
3730 return FALSE; | 3758 return FALSE; |
3731 | 3759 |
3732 if (fp_in == NULL) | 3760 if (fp_in == NULL) |
3735 fp = find_func(fname); | 3763 fp = find_func(fname); |
3736 } | 3764 } |
3737 if (fp != NULL) | 3765 if (fp != NULL) |
3738 { | 3766 { |
3739 for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) | 3767 for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) |
3740 { | 3768 abort = abort || set_ref_in_funccal(fc, copyID); |
3741 if (fc->fc_copyID != copyID) | |
3742 { | |
3743 fc->fc_copyID = copyID; | |
3744 set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); | |
3745 set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); | |
3746 } | |
3747 } | |
3748 } | 3769 } |
3749 vim_free(tofree); | 3770 vim_free(tofree); |
3750 return FALSE; | 3771 return abort; |
3751 } | 3772 } |
3752 | 3773 |
3753 #endif /* FEAT_EVAL */ | 3774 #endif /* FEAT_EVAL */ |