comparison 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
comparison
equal deleted inserted replaced
23556:9f919f241f46 23557:f50ee1ae4d9b
58 garray_T ec_stack; // stack of typval_T values 58 garray_T ec_stack; // stack of typval_T values
59 int ec_frame_idx; // index in ec_stack: context of ec_dfunc_idx 59 int ec_frame_idx; // index in ec_stack: context of ec_dfunc_idx
60 60
61 garray_T *ec_outer_stack; // stack used for closures 61 garray_T *ec_outer_stack; // stack used for closures
62 int ec_outer_frame; // stack frame in ec_outer_stack 62 int ec_outer_frame; // stack frame in ec_outer_stack
63 garray_T *ec_outer_up_stack; // ec_outer_stack one level up
64 int ec_outer_up_frame; // ec_outer_frame one level up
63 65
64 garray_T ec_trystack; // stack of trycmd_T values 66 garray_T ec_trystack; // stack of trycmd_T values
65 int ec_in_catch; // when TRUE in catch or finally block 67 int ec_in_catch; // when TRUE in catch or finally block
66 68
67 int ec_dfunc_idx; // current function index 69 int ec_dfunc_idx; // current function index
147 return OK; 149 return OK;
148 } 150 }
149 151
150 /* 152 /*
151 * Call compiled function "cdf_idx" from compiled code. 153 * Call compiled function "cdf_idx" from compiled code.
154 * This adds a stack frame and sets the instruction pointer to the start of the
155 * called function.
152 * 156 *
153 * Stack has: 157 * Stack has:
154 * - current arguments (already there) 158 * - current arguments (already there)
155 * - omitted optional argument (default values) added here 159 * - omitted optional argument (default values) added here
156 * - stack frame: 160 * - stack frame:
246 STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx; 250 STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx;
247 STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx; 251 STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx;
248 STACK_TV_BOT(2)->vval.v_string = (void *)ectx->ec_outer_stack; 252 STACK_TV_BOT(2)->vval.v_string = (void *)ectx->ec_outer_stack;
249 STACK_TV_BOT(3)->vval.v_number = ectx->ec_outer_frame; 253 STACK_TV_BOT(3)->vval.v_number = ectx->ec_outer_frame;
250 STACK_TV_BOT(4)->vval.v_number = ectx->ec_frame_idx; 254 STACK_TV_BOT(4)->vval.v_number = ectx->ec_frame_idx;
255 // TODO: save ec_outer_up_stack as well?
251 ectx->ec_frame_idx = ectx->ec_stack.ga_len; 256 ectx->ec_frame_idx = ectx->ec_stack.ga_len;
252 257
253 // Initialize local variables 258 // Initialize local variables
254 for (idx = 0; idx < dfunc->df_varcount; ++idx) 259 for (idx = 0; idx < dfunc->df_varcount; ++idx)
255 STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN; 260 STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
264 269
265 if (ufunc->uf_partial != NULL) 270 if (ufunc->uf_partial != NULL)
266 { 271 {
267 ectx->ec_outer_stack = ufunc->uf_partial->pt_ectx_stack; 272 ectx->ec_outer_stack = ufunc->uf_partial->pt_ectx_stack;
268 ectx->ec_outer_frame = ufunc->uf_partial->pt_ectx_frame; 273 ectx->ec_outer_frame = ufunc->uf_partial->pt_ectx_frame;
274 ectx->ec_outer_up_stack = ufunc->uf_partial->pt_outer_stack;
275 ectx->ec_outer_up_frame = ufunc->uf_partial->pt_outer_frame;
276 }
277 else if (ufunc->uf_flags & FC_CLOSURE)
278 {
279 ectx->ec_outer_stack = &ectx->ec_stack;
280 ectx->ec_outer_frame = ectx->ec_frame_idx;
281 ectx->ec_outer_up_stack = ectx->ec_outer_stack;
282 ectx->ec_outer_up_frame = ectx->ec_outer_frame;
269 } 283 }
270 284
271 // Set execution state to the start of the called function. 285 // Set execution state to the start of the called function.
272 ectx->ec_dfunc_idx = cdf_idx; 286 ectx->ec_dfunc_idx = cdf_idx;
273 ectx->ec_instr = dfunc->df_instr; 287 ectx->ec_instr = dfunc->df_instr;
415 { 429 {
416 ++funcstack->fs_refcount; 430 ++funcstack->fs_refcount;
417 pt->pt_funcstack = funcstack; 431 pt->pt_funcstack = funcstack;
418 pt->pt_ectx_stack = &funcstack->fs_ga; 432 pt->pt_ectx_stack = &funcstack->fs_ga;
419 pt->pt_ectx_frame = ectx->ec_frame_idx - top; 433 pt->pt_ectx_frame = ectx->ec_frame_idx - top;
434 pt->pt_outer_stack = ectx->ec_outer_stack;
435 pt->pt_outer_frame = ectx->ec_outer_frame;
420 } 436 }
421 } 437 }
422 } 438 }
423 439
424 for (idx = 0; idx < closure_count; ++idx) 440 for (idx = 0; idx < closure_count; ++idx)
596 return OK; 612 return OK;
597 } 613 }
598 614
599 /* 615 /*
600 * Execute a user defined function. 616 * Execute a user defined function.
617 * If the function is compiled this will add a stack frame and set the
618 * instruction pointer at the start of the function.
619 * Otherwise the function is called here.
601 * "iptr" can be used to replace the instruction with a more efficient one. 620 * "iptr" can be used to replace the instruction with a more efficient one.
602 */ 621 */
603 static int 622 static int
604 call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr) 623 call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
605 { 624 {
741 copy_tv(&pt->pt_argv[i], STACK_TV_BOT(-argcount + i)); 760 copy_tv(&pt->pt_argv[i], STACK_TV_BOT(-argcount + i));
742 } 761 }
743 762
744 if (pt->pt_func != NULL) 763 if (pt->pt_func != NULL)
745 { 764 {
765 int frame_idx = ectx->ec_frame_idx;
746 int ret = call_ufunc(pt->pt_func, argcount, ectx, NULL); 766 int ret = call_ufunc(pt->pt_func, argcount, ectx, NULL);
747 767
748 // closure may need the function context where it was defined 768 if (ectx->ec_frame_idx != frame_idx)
749 ectx->ec_outer_stack = pt->pt_ectx_stack; 769 {
750 ectx->ec_outer_frame = pt->pt_ectx_frame; 770 // call_dfunc() added a stack frame, closure may need the
771 // function context where it was defined.
772 ectx->ec_outer_stack = pt->pt_ectx_stack;
773 ectx->ec_outer_frame = pt->pt_ectx_frame;
774 ectx->ec_outer_up_stack = pt->pt_outer_stack;
775 ectx->ec_outer_up_frame = pt->pt_outer_frame;
776 }
751 777
752 return ret; 778 return ret;
753 } 779 }
754 name = pt->pt_name; 780 name = pt->pt_name;
755 } 781 }
1039 1065
1040 // The closure needs to find arguments and local 1066 // The closure needs to find arguments and local
1041 // variables in the current stack. 1067 // variables in the current stack.
1042 pt->pt_ectx_stack = &ectx->ec_stack; 1068 pt->pt_ectx_stack = &ectx->ec_stack;
1043 pt->pt_ectx_frame = ectx->ec_frame_idx; 1069 pt->pt_ectx_frame = ectx->ec_frame_idx;
1070 pt->pt_outer_stack = ectx->ec_outer_stack;
1071 pt->pt_outer_frame = ectx->ec_outer_frame;
1044 1072
1045 // If this function returns and the closure is still 1073 // If this function returns and the closure is still
1046 // being used, we need to make a copy of the context 1074 // being used, we need to make a copy of the context
1047 // (arguments and local variables). Store a reference 1075 // (arguments and local variables). Store a reference
1048 // to the partial so we can handle that. 1076 // to the partial so we can handle that.
1218 if (partial->pt_ectx_stack == NULL && current_ectx != NULL) 1246 if (partial->pt_ectx_stack == NULL && current_ectx != NULL)
1219 { 1247 {
1220 // TODO: is this always the right way? 1248 // TODO: is this always the right way?
1221 ectx.ec_outer_stack = &current_ectx->ec_stack; 1249 ectx.ec_outer_stack = &current_ectx->ec_stack;
1222 ectx.ec_outer_frame = current_ectx->ec_frame_idx; 1250 ectx.ec_outer_frame = current_ectx->ec_frame_idx;
1251 ectx.ec_outer_up_stack = current_ectx->ec_outer_stack;
1252 ectx.ec_outer_up_frame = current_ectx->ec_outer_frame;
1223 } 1253 }
1224 else 1254 else
1225 { 1255 {
1226 ectx.ec_outer_stack = partial->pt_ectx_stack; 1256 ectx.ec_outer_stack = partial->pt_ectx_stack;
1227 ectx.ec_outer_frame = partial->pt_ectx_frame; 1257 ectx.ec_outer_frame = partial->pt_ectx_frame;
1258 ectx.ec_outer_up_stack = partial->pt_outer_stack;
1259 ectx.ec_outer_up_frame = partial->pt_outer_frame;
1228 } 1260 }
1229 } 1261 }
1230 else if (ufunc->uf_partial != NULL) 1262 else if (ufunc->uf_partial != NULL)
1231 { 1263 {
1232 ectx.ec_outer_stack = ufunc->uf_partial->pt_ectx_stack; 1264 ectx.ec_outer_stack = ufunc->uf_partial->pt_ectx_stack;
1233 ectx.ec_outer_frame = ufunc->uf_partial->pt_ectx_frame; 1265 ectx.ec_outer_frame = ufunc->uf_partial->pt_ectx_frame;
1266 ectx.ec_outer_up_stack = ufunc->uf_partial->pt_outer_stack;
1267 ectx.ec_outer_up_frame = ufunc->uf_partial->pt_outer_frame;
1234 } 1268 }
1235 1269
1236 // dummy frame entries 1270 // dummy frame entries
1237 for (idx = 0; idx < STACK_FRAME_SIZE; ++idx) 1271 for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
1238 { 1272 {
1512 ++ectx.ec_stack.ga_len; 1546 ++ectx.ec_stack.ga_len;
1513 break; 1547 break;
1514 1548
1515 // load variable or argument from outer scope 1549 // load variable or argument from outer scope
1516 case ISN_LOADOUTER: 1550 case ISN_LOADOUTER:
1517 if (GA_GROW(&ectx.ec_stack, 1) == FAIL) 1551 {
1518 goto failed; 1552 typval_T *stack;
1519 copy_tv(STACK_OUT_TV_VAR(iptr->isn_arg.number), 1553 int depth = iptr->isn_arg.outer.outer_depth;
1554
1555 if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
1556 goto failed;
1557 if (depth <= 1)
1558 stack = ((typval_T *)ectx.ec_outer_stack->ga_data)
1559 + ectx.ec_outer_frame;
1560 else if (depth == 2)
1561 stack = ((typval_T *)ectx.ec_outer_up_stack->ga_data)
1562 + ectx.ec_outer_up_frame;
1563 else
1564 {
1565 SOURCING_LNUM = iptr->isn_lnum;
1566 iemsg("LOADOUTER level > 2 not supported yet");
1567 goto failed;
1568 }
1569
1570 copy_tv(stack + STACK_FRAME_SIZE
1571 + iptr->isn_arg.outer.outer_idx,
1520 STACK_TV_BOT(0)); 1572 STACK_TV_BOT(0));
1521 ++ectx.ec_stack.ga_len; 1573 ++ectx.ec_stack.ga_len;
1574 }
1522 break; 1575 break;
1523 1576
1524 // load v: variable 1577 // load v: variable
1525 case ISN_LOADV: 1578 case ISN_LOADV:
1526 if (GA_GROW(&ectx.ec_stack, 1) == FAIL) 1579 if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
1717 break; 1770 break;
1718 1771
1719 // store variable or argument in outer scope 1772 // store variable or argument in outer scope
1720 case ISN_STOREOUTER: 1773 case ISN_STOREOUTER:
1721 --ectx.ec_stack.ga_len; 1774 --ectx.ec_stack.ga_len;
1722 tv = STACK_OUT_TV_VAR(iptr->isn_arg.number); 1775 // TODO: use outer_depth
1776 tv = STACK_OUT_TV_VAR(iptr->isn_arg.outer.outer_idx);
1723 clear_tv(tv); 1777 clear_tv(tv);
1724 *tv = *STACK_TV_BOT(0); 1778 *tv = *STACK_TV_BOT(0);
1725 break; 1779 break;
1726 1780
1727 // store s: variable in old script 1781 // store s: variable in old script
3620 case ISN_ECHOERR: 3674 case ISN_ECHOERR:
3621 smsg("%4d ECHOERR %lld", current, 3675 smsg("%4d ECHOERR %lld", current,
3622 (varnumber_T)(iptr->isn_arg.number)); 3676 (varnumber_T)(iptr->isn_arg.number));
3623 break; 3677 break;
3624 case ISN_LOAD: 3678 case ISN_LOAD:
3625 case ISN_LOADOUTER: 3679 {
3626 {
3627 char *add = iptr->isn_type == ISN_LOAD ? "" : "OUTER";
3628
3629 if (iptr->isn_arg.number < 0) 3680 if (iptr->isn_arg.number < 0)
3630 smsg("%4d LOAD%s arg[%lld]", current, add, 3681 smsg("%4d LOAD arg[%lld]", current,
3631 (varnumber_T)(iptr->isn_arg.number 3682 (varnumber_T)(iptr->isn_arg.number
3632 + STACK_FRAME_SIZE)); 3683 + STACK_FRAME_SIZE));
3633 else 3684 else
3634 smsg("%4d LOAD%s $%lld", current, add, 3685 smsg("%4d LOAD $%lld", current,
3635 (varnumber_T)(iptr->isn_arg.number)); 3686 (varnumber_T)(iptr->isn_arg.number));
3687 }
3688 break;
3689 case ISN_LOADOUTER:
3690 {
3691 if (iptr->isn_arg.number < 0)
3692 smsg("%4d LOADOUTER level %d arg[%d]", current,
3693 iptr->isn_arg.outer.outer_depth,
3694 iptr->isn_arg.outer.outer_idx
3695 + STACK_FRAME_SIZE);
3696 else
3697 smsg("%4d LOADOUTER level %d $%d", current,
3698 iptr->isn_arg.outer.outer_depth,
3699 iptr->isn_arg.outer.outer_idx);
3636 } 3700 }
3637 break; 3701 break;
3638 case ISN_LOADV: 3702 case ISN_LOADV:
3639 smsg("%4d LOADV v:%s", current, 3703 smsg("%4d LOADV v:%s", current,
3640 get_vim_var_name(iptr->isn_arg.number)); 3704 get_vim_var_name(iptr->isn_arg.number));
3697 case ISN_LOADREG: 3761 case ISN_LOADREG:
3698 smsg("%4d LOADREG @%c", current, (int)(iptr->isn_arg.number)); 3762 smsg("%4d LOADREG @%c", current, (int)(iptr->isn_arg.number));
3699 break; 3763 break;
3700 3764
3701 case ISN_STORE: 3765 case ISN_STORE:
3766 if (iptr->isn_arg.number < 0)
3767 smsg("%4d STORE arg[%lld]", current,
3768 iptr->isn_arg.number + STACK_FRAME_SIZE);
3769 else
3770 smsg("%4d STORE $%lld", current, iptr->isn_arg.number);
3771 break;
3702 case ISN_STOREOUTER: 3772 case ISN_STOREOUTER:
3703 { 3773 {
3704 char *add = iptr->isn_type == ISN_STORE ? "" : "OUTER";
3705
3706 if (iptr->isn_arg.number < 0) 3774 if (iptr->isn_arg.number < 0)
3707 smsg("%4d STORE%s arg[%lld]", current, add, 3775 smsg("%4d STOREOUTEr level %d arg[%d]", current,
3708 (varnumber_T)(iptr->isn_arg.number + STACK_FRAME_SIZE)); 3776 iptr->isn_arg.outer.outer_depth,
3777 iptr->isn_arg.outer.outer_idx + STACK_FRAME_SIZE);
3709 else 3778 else
3710 smsg("%4d STORE%s $%lld", current, add, 3779 smsg("%4d STOREOUTER level %d $%d", current,
3711 (varnumber_T)(iptr->isn_arg.number)); 3780 iptr->isn_arg.outer.outer_depth,
3781 iptr->isn_arg.outer.outer_idx);
3712 } 3782 }
3713 break; 3783 break;
3714 case ISN_STOREV: 3784 case ISN_STOREV:
3715 smsg("%4d STOREV v:%s", current, 3785 smsg("%4d STOREV v:%s", current,
3716 get_vim_var_name(iptr->isn_arg.number)); 3786 get_vim_var_name(iptr->isn_arg.number));