Mercurial > vim
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 = ¤t_ectx->ec_stack; | 1249 ectx.ec_outer_stack = ¤t_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)); |