0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

patch 8.2.0677: Vim9: no support for closures

Problem:    Vim9: no support for closures.
Solution:   Find variables in the outer function scope, so long as the scope
            exists.
This commit is contained in:
Bram Moolenaar
2020-05-01 19:29:08 +02:00
parent 37addecc42
commit c8cd2b34d1
8 changed files with 223 additions and 126 deletions

View File

@@ -58,6 +58,9 @@ typedef struct {
garray_T ec_stack; // stack of typval_T values
int ec_frame; // index in ec_stack: context of ec_dfunc_idx
garray_T *ec_outer_stack; // stack used for closures
int ec_outer_frame; // stack frame in ec_outer_stack
garray_T ec_trystack; // stack of trycmd_T values
int ec_in_catch; // when TRUE in catch or finally block
@@ -229,6 +232,10 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
ectx->ec_instr = dfunc->df_instr;
estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
// used for closures
ectx->ec_outer_stack = ufunc->uf_ectx_stack;
ectx->ec_outer_frame = ufunc->uf_ectx_frame;
// Decide where to start execution, handles optional arguments.
init_instr_idx(ufunc, argcount, ectx);
@@ -508,6 +515,9 @@ call_def_function(
// Get pointer to a local variable on the stack. Negative for arguments.
#define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame + STACK_FRAME_SIZE + idx)
// Like STACK_TV_VAR but use the outer scope
#define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) + ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
CLEAR_FIELD(ectx);
ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
if (ga_grow(&ectx.ec_stack, 20) == FAIL)
@@ -786,6 +796,15 @@ call_def_function(
++ectx.ec_stack.ga_len;
break;
// 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),
STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
break;
// load v: variable
case ISN_LOADV:
if (ga_grow(&ectx.ec_stack, 1) == FAIL)
@@ -1304,6 +1323,14 @@ call_def_function(
pt->pt_refcount = 1;
++dfunc->df_ufunc->uf_refcount;
if (dfunc->df_ufunc->uf_flags & FC_CLOSURE)
{
// Closure needs to find local variables in the current
// stack.
dfunc->df_ufunc->uf_ectx_stack = &ectx.ec_stack;
dfunc->df_ufunc->uf_ectx_frame = ectx.ec_frame;
}
if (ga_grow(&ectx.ec_stack, 1) == FAIL)
goto failed;
tv = STACK_TV_BOT(0);
@@ -1862,7 +1889,12 @@ call_def_function(
checktype_T *ct = &iptr->isn_arg.type;
tv = STACK_TV_BOT(ct->ct_off);
if (tv->v_type != ct->ct_type)
// TODO: better type comparison
if (tv->v_type != ct->ct_type
&& !((tv->v_type == VAR_PARTIAL
&& ct->ct_type == VAR_FUNC)
|| (tv->v_type == VAR_FUNC
&& ct->ct_type == VAR_PARTIAL)))
{
semsg(_("E1029: Expected %s but got %s"),
vartype_name(ct->ct_type),
@@ -2029,12 +2061,18 @@ ex_disassemble(exarg_T *eap)
(long long)(iptr->isn_arg.number));
break;
case ISN_LOAD:
if (iptr->isn_arg.number < 0)
smsg("%4d LOAD arg[%lld]", current,
(long long)(iptr->isn_arg.number + STACK_FRAME_SIZE));
else
smsg("%4d LOAD $%lld", current,
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,
(long long)(iptr->isn_arg.number
+ STACK_FRAME_SIZE));
else
smsg("%4d LOAD%s $%lld", current, add,
(long long)(iptr->isn_arg.number));
}
break;
case ISN_LOADV:
smsg("%4d LOADV v:%s", current,