Mercurial > vim
diff src/vim9execute.c @ 23557:f50ee1ae4d9b v8.2.2321
patch 8.2.2321: Vim9: cannot nest closures
Commit: https://github.com/vim/vim/commit/ab360526ef653b139f4b007a0efbdb3410c8fb4b
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Jan 10 14:02:28 2021 +0100
patch 8.2.2321: Vim9: cannot nest closures
Problem: Vim9: cannot nest closures.
Solution: Add the nesting level to ISN_LOADOUTER and ISN_STOREOUTER.
(closes #7150, closes #7635)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 10 Jan 2021 14:15:04 +0100 |
parents | 5c094273c015 |
children | 64dfb69e7d46 |
line wrap: on
line diff
--- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -60,6 +60,8 @@ struct ectx_S { garray_T *ec_outer_stack; // stack used for closures int ec_outer_frame; // stack frame in ec_outer_stack + garray_T *ec_outer_up_stack; // ec_outer_stack one level up + int ec_outer_up_frame; // ec_outer_frame one level up garray_T ec_trystack; // stack of trycmd_T values int ec_in_catch; // when TRUE in catch or finally block @@ -149,6 +151,8 @@ exe_newlist(int count, ectx_T *ectx) /* * Call compiled function "cdf_idx" from compiled code. + * This adds a stack frame and sets the instruction pointer to the start of the + * called function. * * Stack has: * - current arguments (already there) @@ -248,6 +252,7 @@ call_dfunc(int cdf_idx, int argcount_arg STACK_TV_BOT(2)->vval.v_string = (void *)ectx->ec_outer_stack; STACK_TV_BOT(3)->vval.v_number = ectx->ec_outer_frame; STACK_TV_BOT(4)->vval.v_number = ectx->ec_frame_idx; + // TODO: save ec_outer_up_stack as well? ectx->ec_frame_idx = ectx->ec_stack.ga_len; // Initialize local variables @@ -266,6 +271,15 @@ call_dfunc(int cdf_idx, int argcount_arg { ectx->ec_outer_stack = ufunc->uf_partial->pt_ectx_stack; ectx->ec_outer_frame = ufunc->uf_partial->pt_ectx_frame; + ectx->ec_outer_up_stack = ufunc->uf_partial->pt_outer_stack; + ectx->ec_outer_up_frame = ufunc->uf_partial->pt_outer_frame; + } + else if (ufunc->uf_flags & FC_CLOSURE) + { + ectx->ec_outer_stack = &ectx->ec_stack; + ectx->ec_outer_frame = ectx->ec_frame_idx; + ectx->ec_outer_up_stack = ectx->ec_outer_stack; + ectx->ec_outer_up_frame = ectx->ec_outer_frame; } // Set execution state to the start of the called function. @@ -417,6 +431,8 @@ handle_closure_in_use(ectx_T *ectx, int pt->pt_funcstack = funcstack; pt->pt_ectx_stack = &funcstack->fs_ga; pt->pt_ectx_frame = ectx->ec_frame_idx - top; + pt->pt_outer_stack = ectx->ec_outer_stack; + pt->pt_outer_frame = ectx->ec_outer_frame; } } } @@ -598,6 +614,9 @@ call_bfunc(int func_idx, int argcount, e /* * Execute a user defined function. + * If the function is compiled this will add a stack frame and set the + * instruction pointer at the start of the function. + * Otherwise the function is called here. * "iptr" can be used to replace the instruction with a more efficient one. */ static int @@ -743,11 +762,18 @@ call_partial(typval_T *tv, int argcount_ if (pt->pt_func != NULL) { + int frame_idx = ectx->ec_frame_idx; int ret = call_ufunc(pt->pt_func, argcount, ectx, NULL); - // closure may need the function context where it was defined - ectx->ec_outer_stack = pt->pt_ectx_stack; - ectx->ec_outer_frame = pt->pt_ectx_frame; + if (ectx->ec_frame_idx != frame_idx) + { + // call_dfunc() added a stack frame, closure may need the + // function context where it was defined. + ectx->ec_outer_stack = pt->pt_ectx_stack; + ectx->ec_outer_frame = pt->pt_ectx_frame; + ectx->ec_outer_up_stack = pt->pt_outer_stack; + ectx->ec_outer_up_frame = pt->pt_outer_frame; + } return ret; } @@ -1041,6 +1067,8 @@ fill_partial_and_closure(partial_T *pt, // variables in the current stack. pt->pt_ectx_stack = &ectx->ec_stack; pt->pt_ectx_frame = ectx->ec_frame_idx; + pt->pt_outer_stack = ectx->ec_outer_stack; + pt->pt_outer_frame = ectx->ec_outer_frame; // If this function returns and the closure is still // being used, we need to make a copy of the context @@ -1220,17 +1248,23 @@ call_def_function( // TODO: is this always the right way? ectx.ec_outer_stack = ¤t_ectx->ec_stack; ectx.ec_outer_frame = current_ectx->ec_frame_idx; + ectx.ec_outer_up_stack = current_ectx->ec_outer_stack; + ectx.ec_outer_up_frame = current_ectx->ec_outer_frame; } else { ectx.ec_outer_stack = partial->pt_ectx_stack; ectx.ec_outer_frame = partial->pt_ectx_frame; + ectx.ec_outer_up_stack = partial->pt_outer_stack; + ectx.ec_outer_up_frame = partial->pt_outer_frame; } } else if (ufunc->uf_partial != NULL) { ectx.ec_outer_stack = ufunc->uf_partial->pt_ectx_stack; ectx.ec_outer_frame = ufunc->uf_partial->pt_ectx_frame; + ectx.ec_outer_up_stack = ufunc->uf_partial->pt_outer_stack; + ectx.ec_outer_up_frame = ufunc->uf_partial->pt_outer_frame; } // dummy frame entries @@ -1514,11 +1548,30 @@ call_def_function( // load variable or argument from outer scope case ISN_LOADOUTER: - if (GA_GROW(&ectx.ec_stack, 1) == FAIL) - goto failed; - copy_tv(STACK_OUT_TV_VAR(iptr->isn_arg.number), + { + typval_T *stack; + int depth = iptr->isn_arg.outer.outer_depth; + + if (GA_GROW(&ectx.ec_stack, 1) == FAIL) + goto failed; + if (depth <= 1) + stack = ((typval_T *)ectx.ec_outer_stack->ga_data) + + ectx.ec_outer_frame; + else if (depth == 2) + stack = ((typval_T *)ectx.ec_outer_up_stack->ga_data) + + ectx.ec_outer_up_frame; + else + { + SOURCING_LNUM = iptr->isn_lnum; + iemsg("LOADOUTER level > 2 not supported yet"); + goto failed; + } + + copy_tv(stack + STACK_FRAME_SIZE + + iptr->isn_arg.outer.outer_idx, STACK_TV_BOT(0)); - ++ectx.ec_stack.ga_len; + ++ectx.ec_stack.ga_len; + } break; // load v: variable @@ -1719,7 +1772,8 @@ call_def_function( // store variable or argument in outer scope case ISN_STOREOUTER: --ectx.ec_stack.ga_len; - tv = STACK_OUT_TV_VAR(iptr->isn_arg.number); + // TODO: use outer_depth + tv = STACK_OUT_TV_VAR(iptr->isn_arg.outer.outer_idx); clear_tv(tv); *tv = *STACK_TV_BOT(0); break; @@ -3622,17 +3676,27 @@ ex_disassemble(exarg_T *eap) (varnumber_T)(iptr->isn_arg.number)); break; case ISN_LOAD: - case ISN_LOADOUTER: { - char *add = iptr->isn_type == ISN_LOAD ? "" : "OUTER"; - if (iptr->isn_arg.number < 0) - smsg("%4d LOAD%s arg[%lld]", current, add, + smsg("%4d LOAD arg[%lld]", current, (varnumber_T)(iptr->isn_arg.number + STACK_FRAME_SIZE)); else - smsg("%4d LOAD%s $%lld", current, add, - (varnumber_T)(iptr->isn_arg.number)); + smsg("%4d LOAD $%lld", current, + (varnumber_T)(iptr->isn_arg.number)); + } + break; + case ISN_LOADOUTER: + { + if (iptr->isn_arg.number < 0) + smsg("%4d LOADOUTER level %d arg[%d]", current, + iptr->isn_arg.outer.outer_depth, + iptr->isn_arg.outer.outer_idx + + STACK_FRAME_SIZE); + else + smsg("%4d LOADOUTER level %d $%d", current, + iptr->isn_arg.outer.outer_depth, + iptr->isn_arg.outer.outer_idx); } break; case ISN_LOADV: @@ -3699,16 +3763,22 @@ ex_disassemble(exarg_T *eap) break; case ISN_STORE: + if (iptr->isn_arg.number < 0) + smsg("%4d STORE arg[%lld]", current, + iptr->isn_arg.number + STACK_FRAME_SIZE); + else + smsg("%4d STORE $%lld", current, iptr->isn_arg.number); + break; case ISN_STOREOUTER: { - char *add = iptr->isn_type == ISN_STORE ? "" : "OUTER"; - if (iptr->isn_arg.number < 0) - smsg("%4d STORE%s arg[%lld]", current, add, - (varnumber_T)(iptr->isn_arg.number + STACK_FRAME_SIZE)); + smsg("%4d STOREOUTEr level %d arg[%d]", current, + iptr->isn_arg.outer.outer_depth, + iptr->isn_arg.outer.outer_idx + STACK_FRAME_SIZE); else - smsg("%4d STORE%s $%lld", current, add, - (varnumber_T)(iptr->isn_arg.number)); + smsg("%4d STOREOUTER level %d $%d", current, + iptr->isn_arg.outer.outer_depth, + iptr->isn_arg.outer.outer_idx); } break; case ISN_STOREV: