Mercurial > vim
comparison src/vim9execute.c @ 23249:43532077b5ff v8.2.2170
patch 8.2.2170: Vim9: a global function defined in a :def function fails
Commit: https://github.com/vim/vim/commit/f112f30a82f17114d8b08a0fb90928cd19440581
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Dec 20 17:47:52 2020 +0100
patch 8.2.2170: Vim9: a global function defined in a :def function fails
Problem: Vim9: a global function defined in a :def function fails if it
uses the context.
Solution: Create a partial to store the closure context. (see #7410)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 20 Dec 2020 18:00:06 +0100 |
parents | 657216220293 |
children | 35583da6397e |
comparison
equal
deleted
inserted
replaced
23248:a0e114c6b39e | 23249:43532077b5ff |
---|---|
840 return FAIL; | 840 return FAIL; |
841 } | 841 } |
842 return call_partial(&v->di_tv, argcount, ectx); | 842 return call_partial(&v->di_tv, argcount, ectx); |
843 } | 843 } |
844 return res; | 844 return res; |
845 } | |
846 | |
847 /* | |
848 * When a function reference is used, fill a partial with the information | |
849 * needed, especially when it is used as a closure. | |
850 */ | |
851 static int | |
852 fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx) | |
853 { | |
854 pt->pt_func = ufunc; | |
855 pt->pt_refcount = 1; | |
856 | |
857 if (pt->pt_func->uf_flags & FC_CLOSURE) | |
858 { | |
859 dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) | |
860 + ectx->ec_dfunc_idx; | |
861 | |
862 // The closure needs to find arguments and local | |
863 // variables in the current stack. | |
864 pt->pt_ectx_stack = &ectx->ec_stack; | |
865 pt->pt_ectx_frame = ectx->ec_frame_idx; | |
866 | |
867 // If this function returns and the closure is still | |
868 // being used, we need to make a copy of the context | |
869 // (arguments and local variables). Store a reference | |
870 // to the partial so we can handle that. | |
871 if (ga_grow(&ectx->ec_funcrefs, 1) == FAIL) | |
872 { | |
873 vim_free(pt); | |
874 return FAIL; | |
875 } | |
876 // Extra variable keeps the count of closures created | |
877 // in the current function call. | |
878 ++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx | |
879 + STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number; | |
880 | |
881 ((partial_T **)ectx->ec_funcrefs.ga_data) | |
882 [ectx->ec_funcrefs.ga_len] = pt; | |
883 ++pt->pt_refcount; | |
884 ++ectx->ec_funcrefs.ga_len; | |
885 } | |
886 ++pt->pt_func->uf_refcount; | |
887 return OK; | |
845 } | 888 } |
846 | 889 |
847 /* | 890 /* |
848 * Call a "def" function from old Vim script. | 891 * Call a "def" function from old Vim script. |
849 * Return OK or FAIL. | 892 * Return OK or FAIL. |
1000 else | 1043 else |
1001 { | 1044 { |
1002 ectx.ec_outer_stack = partial->pt_ectx_stack; | 1045 ectx.ec_outer_stack = partial->pt_ectx_stack; |
1003 ectx.ec_outer_frame = partial->pt_ectx_frame; | 1046 ectx.ec_outer_frame = partial->pt_ectx_frame; |
1004 } | 1047 } |
1048 } | |
1049 else if (ufunc->uf_partial != NULL) | |
1050 { | |
1051 ectx.ec_outer_stack = ufunc->uf_partial->pt_ectx_stack; | |
1052 ectx.ec_outer_frame = ufunc->uf_partial->pt_ectx_frame; | |
1005 } | 1053 } |
1006 | 1054 |
1007 // dummy frame entries | 1055 // dummy frame entries |
1008 for (idx = 0; idx < STACK_FRAME_SIZE; ++idx) | 1056 for (idx = 0; idx < STACK_FRAME_SIZE; ++idx) |
1009 { | 1057 { |
1967 break; | 2015 break; |
1968 | 2016 |
1969 // push a function reference to a compiled function | 2017 // push a function reference to a compiled function |
1970 case ISN_FUNCREF: | 2018 case ISN_FUNCREF: |
1971 { | 2019 { |
1972 partial_T *pt = NULL; | 2020 partial_T *pt = ALLOC_CLEAR_ONE(partial_T); |
1973 dfunc_T *pt_dfunc; | 2021 dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data) |
1974 | 2022 + iptr->isn_arg.funcref.fr_func; |
1975 pt = ALLOC_CLEAR_ONE(partial_T); | 2023 |
1976 if (pt == NULL) | 2024 if (pt == NULL) |
1977 goto failed; | 2025 goto failed; |
1978 if (GA_GROW(&ectx.ec_stack, 1) == FAIL) | 2026 if (GA_GROW(&ectx.ec_stack, 1) == FAIL) |
1979 { | 2027 { |
1980 vim_free(pt); | 2028 vim_free(pt); |
1981 goto failed; | 2029 goto failed; |
1982 } | 2030 } |
1983 pt_dfunc = ((dfunc_T *)def_functions.ga_data) | 2031 if (fill_partial_and_closure(pt, pt_dfunc->df_ufunc, |
1984 + iptr->isn_arg.funcref.fr_func; | 2032 &ectx) == FAIL) |
1985 pt->pt_func = pt_dfunc->df_ufunc; | 2033 goto failed; |
1986 pt->pt_refcount = 1; | |
1987 | |
1988 if (pt_dfunc->df_ufunc->uf_flags & FC_CLOSURE) | |
1989 { | |
1990 dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) | |
1991 + ectx.ec_dfunc_idx; | |
1992 | |
1993 // The closure needs to find arguments and local | |
1994 // variables in the current stack. | |
1995 pt->pt_ectx_stack = &ectx.ec_stack; | |
1996 pt->pt_ectx_frame = ectx.ec_frame_idx; | |
1997 | |
1998 // If this function returns and the closure is still | |
1999 // being used, we need to make a copy of the context | |
2000 // (arguments and local variables). Store a reference | |
2001 // to the partial so we can handle that. | |
2002 if (ga_grow(&ectx.ec_funcrefs, 1) == FAIL) | |
2003 { | |
2004 vim_free(pt); | |
2005 goto failed; | |
2006 } | |
2007 // Extra variable keeps the count of closures created | |
2008 // in the current function call. | |
2009 tv = STACK_TV_VAR(dfunc->df_varcount); | |
2010 ++tv->vval.v_number; | |
2011 | |
2012 ((partial_T **)ectx.ec_funcrefs.ga_data) | |
2013 [ectx.ec_funcrefs.ga_len] = pt; | |
2014 ++pt->pt_refcount; | |
2015 ++ectx.ec_funcrefs.ga_len; | |
2016 } | |
2017 ++pt_dfunc->df_ufunc->uf_refcount; | |
2018 | 2034 |
2019 tv = STACK_TV_BOT(0); | 2035 tv = STACK_TV_BOT(0); |
2020 ++ectx.ec_stack.ga_len; | 2036 ++ectx.ec_stack.ga_len; |
2021 tv->vval.v_partial = pt; | 2037 tv->vval.v_partial = pt; |
2022 tv->v_type = VAR_PARTIAL; | 2038 tv->v_type = VAR_PARTIAL; |
2026 | 2042 |
2027 // Create a global function from a lambda. | 2043 // Create a global function from a lambda. |
2028 case ISN_NEWFUNC: | 2044 case ISN_NEWFUNC: |
2029 { | 2045 { |
2030 newfunc_T *newfunc = &iptr->isn_arg.newfunc; | 2046 newfunc_T *newfunc = &iptr->isn_arg.newfunc; |
2031 | 2047 ufunc_T *new_ufunc; |
2032 copy_func(newfunc->nf_lambda, newfunc->nf_global); | 2048 |
2049 new_ufunc = copy_func( | |
2050 newfunc->nf_lambda, newfunc->nf_global); | |
2051 if (new_ufunc != NULL | |
2052 && (new_ufunc->uf_flags & FC_CLOSURE)) | |
2053 { | |
2054 partial_T *pt = ALLOC_CLEAR_ONE(partial_T); | |
2055 | |
2056 // Need to create a partial to store the context of the | |
2057 // function. | |
2058 if (pt == NULL) | |
2059 goto failed; | |
2060 if (fill_partial_and_closure(pt, new_ufunc, | |
2061 &ectx) == FAIL) | |
2062 goto failed; | |
2063 new_ufunc->uf_partial = pt; | |
2064 --pt->pt_refcount; // not referenced here | |
2065 } | |
2033 } | 2066 } |
2034 break; | 2067 break; |
2035 | 2068 |
2036 // List functions | 2069 // List functions |
2037 case ISN_DEF: | 2070 case ISN_DEF: |
3112 while (ectx.ec_frame_idx != initial_frame_idx) | 3145 while (ectx.ec_frame_idx != initial_frame_idx) |
3113 func_return(&ectx); | 3146 func_return(&ectx); |
3114 | 3147 |
3115 // Deal with any remaining closures, they may be in use somewhere. | 3148 // Deal with any remaining closures, they may be in use somewhere. |
3116 if (ectx.ec_funcrefs.ga_len > 0) | 3149 if (ectx.ec_funcrefs.ga_len > 0) |
3150 { | |
3117 handle_closure_in_use(&ectx, FALSE); | 3151 handle_closure_in_use(&ectx, FALSE); |
3152 ga_clear(&ectx.ec_funcrefs); // TODO: should not be needed? | |
3153 } | |
3118 | 3154 |
3119 estack_pop(); | 3155 estack_pop(); |
3120 current_sctx = save_current_sctx; | 3156 current_sctx = save_current_sctx; |
3121 | 3157 |
3122 if (*msg_list != NULL && saved_msg_list != NULL) | 3158 if (*msg_list != NULL && saved_msg_list != NULL) |