0
0
mirror of https://github.com/vim/vim.git synced 2025-09-26 04:04:07 -04:00

patch 8.2.1734: Vim9: cannot use a funcref for a closure twice

Problem:    Vim9: cannot use a funcref for a closure twice.
Solution:   Instead of putting the funcref on the stack use a growarray on the
            execution context.
This commit is contained in:
Bram Moolenaar
2020-09-23 21:57:23 +02:00
parent 4ee711f213
commit 148ce7ae62
6 changed files with 93 additions and 64 deletions

View File

@@ -708,7 +708,7 @@ def Test_disassemble_lambda()
let instr = execute('disassemble WithLambda') let instr = execute('disassemble WithLambda')
assert_match('WithLambda\_s*' .. assert_match('WithLambda\_s*' ..
'let F = {a -> "X" .. a .. "X"}\_s*' .. 'let F = {a -> "X" .. a .. "X"}\_s*' ..
'\d FUNCREF <lambda>\d\+ $1\_s*' .. '\d FUNCREF <lambda>\d\+\_s*' ..
'\d STORE $0\_s*' .. '\d STORE $0\_s*' ..
'return F("x")\_s*' .. 'return F("x")\_s*' ..
'\d PUSHS "x"\_s*' .. '\d PUSHS "x"\_s*' ..

View File

@@ -1367,7 +1367,7 @@ def Test_double_closure_fails()
enddef enddef
Func() Func()
END END
CheckScriptFailure(lines, 'Multiple closures not supported yet') CheckScriptSuccess(lines)
enddef enddef
def Test_sort_return_type() def Test_sort_return_type()

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1734,
/**/ /**/
1733, 1733,
/**/ /**/

View File

@@ -244,7 +244,6 @@ typedef struct {
// arguments to ISN_FUNCREF // arguments to ISN_FUNCREF
typedef struct { typedef struct {
int fr_func; // function index int fr_func; // function index
int fr_var_idx; // variable to store partial
} funcref_T; } funcref_T;
// arguments to ISN_NEWFUNC // arguments to ISN_NEWFUNC
@@ -323,7 +322,7 @@ struct dfunc_S {
int df_instr_count; int df_instr_count;
int df_varcount; // number of local variables int df_varcount; // number of local variables
int df_closure_count; // number of closures created int df_has_closure; // one if a closure was created
}; };
// Number of entries used by stack frame for a function call. // Number of entries used by stack frame for a function call.

View File

@@ -126,8 +126,8 @@ struct cctx_S {
garray_T ctx_locals; // currently visible local variables garray_T ctx_locals; // currently visible local variables
int ctx_locals_count; // total number of local variables int ctx_locals_count; // total number of local variables
int ctx_closure_count; // number of closures created in the int ctx_has_closure; // set to one if a closures was created in
// function // the function
garray_T ctx_imports; // imported items garray_T ctx_imports; // imported items
@@ -1273,7 +1273,7 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL) if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
return FAIL; return FAIL;
isn->isn_arg.funcref.fr_func = ufunc->uf_dfunc_idx; isn->isn_arg.funcref.fr_func = ufunc->uf_dfunc_idx;
isn->isn_arg.funcref.fr_var_idx = cctx->ctx_closure_count++; cctx->ctx_has_closure = 1;
if (ga_grow(stack, 1) == FAIL) if (ga_grow(stack, 1) == FAIL)
return FAIL; return FAIL;
@@ -7138,7 +7138,7 @@ nextline:
dfunc->df_instr = instr->ga_data; dfunc->df_instr = instr->ga_data;
dfunc->df_instr_count = instr->ga_len; dfunc->df_instr_count = instr->ga_len;
dfunc->df_varcount = cctx.ctx_locals_count; dfunc->df_varcount = cctx.ctx_locals_count;
dfunc->df_closure_count = cctx.ctx_closure_count; dfunc->df_has_closure = cctx.ctx_has_closure;
if (cctx.ctx_outer_used) if (cctx.ctx_outer_used)
ufunc->uf_flags |= FC_CLOSURE; ufunc->uf_flags |= FC_CLOSURE;
ufunc->uf_def_status = UF_COMPILED; ufunc->uf_def_status = UF_COMPILED;
@@ -7312,7 +7312,8 @@ delete_instr(isn_T *isn)
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ isn->isn_arg.dfunc.cdf_idx; + isn->isn_arg.dfunc.cdf_idx;
if (func_name_refcount(dfunc->df_ufunc->uf_name)) if (dfunc->df_ufunc != NULL
&& func_name_refcount(dfunc->df_ufunc->uf_name))
func_ptr_unref(dfunc->df_ufunc); func_ptr_unref(dfunc->df_ufunc);
} }
break; break;

View File

@@ -67,6 +67,8 @@ typedef struct {
int ec_dfunc_idx; // current function index int ec_dfunc_idx; // current function index
isn_T *ec_instr; // array with instructions isn_T *ec_instr; // array with instructions
int ec_iidx; // index in ec_instr: instruction to execute int ec_iidx; // index in ec_instr: instruction to execute
garray_T ec_funcrefs; // partials that might be a closure
} ectx_T; } ectx_T;
// Get pointer to item relative to the bottom of the stack, -1 is the last one. // Get pointer to item relative to the bottom of the stack, -1 is the last one.
@@ -165,6 +167,7 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
ufunc_T *ufunc = dfunc->df_ufunc; ufunc_T *ufunc = dfunc->df_ufunc;
int arg_to_add; int arg_to_add;
int vararg_count = 0; int vararg_count = 0;
int varcount;
int idx; int idx;
estack_T *entry; estack_T *entry;
@@ -212,8 +215,16 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
semsg(_(e_nr_arguments_too_many), -arg_to_add); semsg(_(e_nr_arguments_too_many), -arg_to_add);
return FAIL; return FAIL;
} }
if (ga_grow(&ectx->ec_stack, arg_to_add + 3
+ dfunc->df_varcount + dfunc->df_closure_count) == FAIL) // Reserve space for:
// - missing arguments
// - stack frame
// - local variables
// - if needed: a counter for number of closures created in
// ectx->ec_funcrefs.
varcount = dfunc->df_varcount + dfunc->df_has_closure;
if (ga_grow(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE + varcount)
== FAIL)
return FAIL; return FAIL;
// Move the vararg-list to below the missing optional arguments. // Move the vararg-list to below the missing optional arguments.
@@ -232,10 +243,16 @@ call_dfunc(int cdf_idx, int argcount_arg, ectx_T *ectx)
ectx->ec_frame_idx = ectx->ec_stack.ga_len; ectx->ec_frame_idx = ectx->ec_stack.ga_len;
// Initialize local variables // Initialize local variables
for (idx = 0; idx < dfunc->df_varcount + dfunc->df_closure_count; ++idx) for (idx = 0; idx < dfunc->df_varcount; ++idx)
STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN; STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
ectx->ec_stack.ga_len += STACK_FRAME_SIZE if (dfunc->df_has_closure)
+ dfunc->df_varcount + dfunc->df_closure_count; {
typval_T *tv = STACK_TV_BOT(STACK_FRAME_SIZE + dfunc->df_varcount);
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
}
ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
// Set execution state to the start of the called function. // Set execution state to the start of the called function.
ectx->ec_dfunc_idx = cdf_idx; ectx->ec_dfunc_idx = cdf_idx;
@@ -275,22 +292,30 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
int idx; int idx;
typval_T *tv; typval_T *tv;
int closure_in_use = FALSE; int closure_in_use = FALSE;
garray_T *gap = &ectx->ec_funcrefs;
varnumber_T closure_count;
if (dfunc->df_ufunc == NULL) if (dfunc->df_ufunc == NULL)
// function was freed return OK; // function was freed
return OK; if (dfunc->df_has_closure == 0)
return OK; // no closures
tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE + dfunc->df_varcount);
closure_count = tv->vval.v_number;
if (closure_count == 0)
return OK; // no funcrefs created
argcount = ufunc_argcount(dfunc->df_ufunc); argcount = ufunc_argcount(dfunc->df_ufunc);
top = ectx->ec_frame_idx - argcount; top = ectx->ec_frame_idx - argcount;
// Check if any created closure is still in use. // Check if any created closure is still in use.
for (idx = 0; idx < dfunc->df_closure_count; ++idx) for (idx = 0; idx < closure_count; ++idx)
{ {
tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len
+ dfunc->df_varcount + idx); - closure_count + idx];
if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL
&& tv->vval.v_partial->pt_refcount > 1) if (pt->pt_refcount > 1)
{ {
int refcount = tv->vval.v_partial->pt_refcount; int refcount = pt->pt_refcount;
int i; int i;
// A Reference in a local variables doesn't count, it gets // A Reference in a local variables doesn't count, it gets
@@ -299,8 +324,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
{ {
typval_T *stv = STACK_TV(ectx->ec_frame_idx typval_T *stv = STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_SIZE + i); + STACK_FRAME_SIZE + i);
if (stv->v_type == VAR_PARTIAL if (stv->v_type == VAR_PARTIAL && pt == stv->vval.v_partial)
&& tv->vval.v_partial == stv->vval.v_partial)
--refcount; --refcount;
} }
if (refcount > 1) if (refcount > 1)
@@ -355,46 +379,43 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL) if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL)
{ {
int i; int i;
typval_T *ctv;
for (i = 0; i < dfunc->df_closure_count; ++i) for (i = 0; i < closure_count; ++i)
{ {
ctv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len
+ dfunc->df_varcount + i); - closure_count + i];
if (tv->vval.v_partial == ctv->vval.v_partial) if (tv->vval.v_partial == pt)
break; break;
} }
if (i < dfunc->df_closure_count) if (i < closure_count)
{
(stack + argcount + STACK_FRAME_SIZE + idx)->v_type =
VAR_UNKNOWN;
continue; continue;
}
} }
*(stack + argcount + STACK_FRAME_SIZE + idx) = *tv; *(stack + argcount + STACK_FRAME_SIZE + idx) = *tv;
tv->v_type = VAR_UNKNOWN; tv->v_type = VAR_UNKNOWN;
} }
for (idx = 0; idx < dfunc->df_closure_count; ++idx) for (idx = 0; idx < closure_count; ++idx)
{ {
tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len
+ dfunc->df_varcount + idx); - closure_count + idx];
if (tv->v_type == VAR_PARTIAL) if (pt->pt_refcount > 1)
{ {
partial_T *partial = tv->vval.v_partial; ++funcstack->fs_refcount;
pt->pt_funcstack = funcstack;
if (partial->pt_refcount > 1) pt->pt_ectx_stack = &funcstack->fs_ga;
{ pt->pt_ectx_frame = ectx->ec_frame_idx - top;
++funcstack->fs_refcount;
partial->pt_funcstack = funcstack;
partial->pt_ectx_stack = &funcstack->fs_ga;
partial->pt_ectx_frame = ectx->ec_frame_idx - top;
}
} }
} }
} }
for (idx = 0; idx < closure_count; ++idx)
partial_unref(((partial_T **)gap->ga_data)[gap->ga_len
- closure_count + idx]);
gap->ga_len -= closure_count;
if (gap->ga_len == 0)
ga_clear(gap);
return OK; return OK;
} }
@@ -809,6 +830,7 @@ call_def_function(
if (ga_grow(&ectx.ec_stack, 20) == FAIL) if (ga_grow(&ectx.ec_stack, 20) == FAIL)
return FAIL; return FAIL;
ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10); ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10);
ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10);
// Put arguments on the stack. // Put arguments on the stack.
for (idx = 0; idx < argc; ++idx) for (idx = 0; idx < argc; ++idx)
@@ -896,14 +918,19 @@ call_def_function(
} }
{ {
// Reserve space for local variables and closure references. // Reserve space for local variables and any closure reference count.
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx; + ufunc->uf_dfunc_idx;
int count = dfunc->df_varcount + dfunc->df_closure_count;
for (idx = 0; idx < count; ++idx) for (idx = 0; idx < dfunc->df_varcount; ++idx)
STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN; STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
ectx.ec_stack.ga_len += count; ectx.ec_stack.ga_len += dfunc->df_varcount;
if (dfunc->df_has_closure)
{
STACK_TV_VAR(idx)->v_type = VAR_NUMBER;
STACK_TV_VAR(idx)->vval.v_number = 0;
++ectx.ec_stack.ga_len;
}
ectx.ec_instr = dfunc->df_instr; ectx.ec_instr = dfunc->df_instr;
} }
@@ -1812,7 +1839,6 @@ call_def_function(
+ iptr->isn_arg.funcref.fr_func; + iptr->isn_arg.funcref.fr_func;
pt->pt_func = pt_dfunc->df_ufunc; pt->pt_func = pt_dfunc->df_ufunc;
pt->pt_refcount = 1; pt->pt_refcount = 1;
++pt_dfunc->df_ufunc->uf_refcount;
if (pt_dfunc->df_ufunc->uf_flags & FC_CLOSURE) if (pt_dfunc->df_ufunc->uf_flags & FC_CLOSURE)
{ {
@@ -1825,23 +1851,25 @@ call_def_function(
pt->pt_ectx_frame = ectx.ec_frame_idx; pt->pt_ectx_frame = ectx.ec_frame_idx;
// If this function returns and the closure is still // If this function returns and the closure is still
// used, we need to make a copy of the context // being used, we need to make a copy of the context
// (arguments and local variables). Store a reference // (arguments and local variables). Store a reference
// to the partial so we can handle that. // to the partial so we can handle that.
++pt->pt_refcount; if (ga_grow(&ectx.ec_funcrefs, 1) == FAIL)
tv = STACK_TV_VAR(dfunc->df_varcount
+ iptr->isn_arg.funcref.fr_var_idx);
if (tv->v_type == VAR_PARTIAL)
{ {
// TODO: use a garray_T on ectx.
SOURCING_LNUM = iptr->isn_lnum;
emsg("Multiple closures not supported yet");
vim_free(pt); vim_free(pt);
goto failed; goto failed;
} }
tv->v_type = VAR_PARTIAL; // Extra variable keeps the count of closures created
tv->vval.v_partial = pt; // in the current function call.
tv = STACK_TV_VAR(dfunc->df_varcount);
++tv->vval.v_number;
((partial_T **)ectx.ec_funcrefs.ga_data)
[ectx.ec_funcrefs.ga_len] = pt;
++pt->pt_refcount;
++ectx.ec_funcrefs.ga_len;
} }
++pt_dfunc->df_ufunc->uf_refcount;
tv = STACK_TV_BOT(0); tv = STACK_TV_BOT(0);
++ectx.ec_stack.ga_len; ++ectx.ec_stack.ga_len;
@@ -3124,8 +3152,7 @@ ex_disassemble(exarg_T *eap)
dfunc_T *df = ((dfunc_T *)def_functions.ga_data) dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_func; + funcref->fr_func;
smsg("%4d FUNCREF %s $%d", current, df->df_ufunc->uf_name, smsg("%4d FUNCREF %s", current, df->df_ufunc->uf_name);
funcref->fr_var_idx + dfunc->df_varcount);
} }
break; break;