mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.0.0481: in :def function all closures in loop get the same variables
Problem: In a :def function all closures in a loop get the same variables. Solution: Use a separate list of variables for LOADOUTER and STOREOUTER. Not copied at end of loop yet.
This commit is contained in:
@@ -16,7 +16,7 @@ int func_is_global(ufunc_T *ufunc);
|
||||
int func_requires_g_prefix(ufunc_T *ufunc);
|
||||
int func_name_refcount(char_u *name);
|
||||
void func_clear_free(ufunc_T *fp, int force);
|
||||
int copy_func(char_u *lambda, char_u *global, ectx_T *ectx);
|
||||
int copy_lambda_to_global_func(char_u *lambda, char_u *global, short loop_var_idx, short loop_var_count, ectx_T *ectx);
|
||||
int funcdepth_increment(void);
|
||||
void funcdepth_decrement(void);
|
||||
int funcdepth_get(void);
|
||||
|
@@ -11,6 +11,8 @@ char_u *compile_for(char_u *arg_start, cctx_T *cctx);
|
||||
char_u *compile_endfor(char_u *arg, cctx_T *cctx);
|
||||
char_u *compile_while(char_u *arg, cctx_T *cctx);
|
||||
char_u *compile_endwhile(char_u *arg, cctx_T *cctx);
|
||||
short get_loop_var_info(cctx_T *cctx, short *loop_var_idx);
|
||||
int get_loop_var_idx(cctx_T *cctx);
|
||||
char_u *compile_continue(char_u *arg, cctx_T *cctx);
|
||||
char_u *compile_break(char_u *arg, cctx_T *cctx);
|
||||
char_u *compile_block(char_u *arg, cctx_T *cctx);
|
||||
|
@@ -9,7 +9,7 @@ void restore_current_ectx(ectx_T *ectx);
|
||||
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
|
||||
char_u *char_from_string(char_u *str, varnumber_T index);
|
||||
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
|
||||
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
|
||||
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, short loop_var_idx, short loop_var_count, ectx_T *ectx);
|
||||
int may_load_script(int sid, int *loaded);
|
||||
typval_T *lookup_debug_var(char_u *name);
|
||||
int may_break_in_function(ufunc_T *ufunc);
|
||||
|
@@ -31,7 +31,7 @@ int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
|
||||
int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
|
||||
int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
|
||||
int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type);
|
||||
int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, type_T *type);
|
||||
int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_idx, type_T *type);
|
||||
int generate_LOADV(cctx_T *cctx, char_u *name);
|
||||
int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
|
||||
int generate_LOCKCONST(cctx_T *cctx);
|
||||
@@ -40,7 +40,7 @@ int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type
|
||||
int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
|
||||
int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
|
||||
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
|
||||
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
|
||||
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name, short loop_var_idx, short loop_var_count);
|
||||
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
|
||||
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
|
||||
int generate_WHILE(cctx_T *cctx, int funcref_idx);
|
||||
|
@@ -1656,7 +1656,7 @@ typedef enum {
|
||||
|
||||
/*
|
||||
* Structure to hold info for a user function.
|
||||
* When adding a field check copy_func().
|
||||
* When adding a field check copy_lambda_to_global_func().
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
@@ -1741,7 +1741,8 @@ typedef struct
|
||||
#define FC_NOARGS 0x200 // no a: variables in lambda
|
||||
#define FC_VIM9 0x400 // defined in vim9 script file
|
||||
#define FC_CFUNC 0x800 // defined as Lua C func
|
||||
#define FC_COPY 0x1000 // copy of another function by copy_func()
|
||||
#define FC_COPY 0x1000 // copy of another function by
|
||||
// copy_lambda_to_global_func()
|
||||
#define FC_LAMBDA 0x2000 // one line "return {expr}"
|
||||
|
||||
#define MAX_FUNC_ARGS 20 // maximum number of function arguments
|
||||
@@ -2096,10 +2097,17 @@ struct funcstack_S
|
||||
|
||||
typedef struct outer_S outer_T;
|
||||
struct outer_S {
|
||||
garray_T *out_stack; // stack from outer scope
|
||||
garray_T *out_stack; // stack from outer scope, or a copy
|
||||
// containing only arguments and local vars
|
||||
int out_frame_idx; // index of stack frame in out_stack
|
||||
outer_T *out_up; // outer scope of outer scope or NULL
|
||||
partial_T *out_up_partial; // partial owning out_up or NULL
|
||||
|
||||
garray_T *out_loop_stack; // stack from outer scope, or a copy
|
||||
// containing only vars inside the loop
|
||||
short out_loop_var_idx; // first variable defined in a loop
|
||||
// in out_loop_stack
|
||||
short out_loop_var_count; // number of variables defined in a loop
|
||||
};
|
||||
|
||||
struct partial_S
|
||||
|
@@ -2452,7 +2452,12 @@ func_clear_free(ufunc_T *fp, int force)
|
||||
* This is for when a compiled function defines a global function.
|
||||
*/
|
||||
int
|
||||
copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
|
||||
copy_lambda_to_global_func(
|
||||
char_u *lambda,
|
||||
char_u *global,
|
||||
short loop_var_idx,
|
||||
short loop_var_count,
|
||||
ectx_T *ectx)
|
||||
{
|
||||
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
|
||||
ufunc_T *fp = NULL;
|
||||
@@ -2519,7 +2524,8 @@ copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
|
||||
|
||||
if (pt == NULL)
|
||||
goto failed;
|
||||
if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
|
||||
if (fill_partial_and_closure(pt, ufunc, loop_var_idx, loop_var_count,
|
||||
ectx) == FAIL)
|
||||
{
|
||||
vim_free(pt);
|
||||
goto failed;
|
||||
|
@@ -703,6 +703,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
481,
|
||||
/**/
|
||||
480,
|
||||
/**/
|
||||
|
24
src/vim9.h
24
src/vim9.h
@@ -354,16 +354,29 @@ typedef struct {
|
||||
int ul_forceit; // forceit flag
|
||||
} unlet_T;
|
||||
|
||||
// extra arguments for funcref_T
|
||||
typedef struct {
|
||||
char_u *fre_func_name; // function name for legacy function
|
||||
short fre_loop_var_idx; // index of first variable inside loop
|
||||
short fre_loop_var_count; // number of variables inside loop
|
||||
} funcref_extra_T;
|
||||
|
||||
// arguments to ISN_FUNCREF
|
||||
typedef struct {
|
||||
int fr_dfunc_idx; // function index for :def function
|
||||
char_u *fr_func_name; // function name for legacy function
|
||||
int fr_dfunc_idx; // function index for :def function
|
||||
funcref_extra_T *fr_extra; // optional extra information
|
||||
} funcref_T;
|
||||
|
||||
// arguments to ISN_NEWFUNC
|
||||
typedef struct {
|
||||
char_u *nf_lambda; // name of the lambda already defined
|
||||
char_u *nf_global; // name of the global function to be created
|
||||
char_u *nfa_lambda; // name of the lambda already defined
|
||||
char_u *nfa_global; // name of the global function to be created
|
||||
short nfa_loop_var_idx; // index of first variable inside loop
|
||||
short nfa_loop_var_count; // number of variables inside loop
|
||||
} newfuncarg_T;
|
||||
|
||||
typedef struct {
|
||||
newfuncarg_T *nf_arg;
|
||||
} newfunc_T;
|
||||
|
||||
// arguments to ISN_CHECKLEN
|
||||
@@ -401,6 +414,8 @@ typedef struct {
|
||||
int outer_depth; // nesting level, stack frames to go up
|
||||
} isn_outer_T;
|
||||
|
||||
#define OUTER_LOOP_DEPTH -9 // used for outer_depth for loop variables
|
||||
|
||||
// arguments to ISN_SUBSTITUTE
|
||||
typedef struct {
|
||||
char_u *subs_cmd; // :s command
|
||||
@@ -677,6 +692,7 @@ typedef struct {
|
||||
char_u *lv_name;
|
||||
type_T *lv_type;
|
||||
int lv_idx; // index of the variable on the stack
|
||||
int lv_loop_idx; // index of first variable inside a loop or -1
|
||||
int lv_from_outer; // nesting level, using ctx_outer scope
|
||||
int lv_const; // when TRUE cannot be assigned to
|
||||
int lv_arg; // when TRUE this is an argument
|
||||
|
@@ -1245,6 +1245,49 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
|
||||
return arg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current information about variables declared inside a loop.
|
||||
* Returns zero if there are none, otherwise the count.
|
||||
* "loop_var_idx" is then set to the index of the first variable.
|
||||
*/
|
||||
short
|
||||
get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
|
||||
{
|
||||
scope_T *scope = cctx->ctx_scope;
|
||||
int start_local_count;
|
||||
|
||||
while (scope != NULL && scope->se_type != WHILE_SCOPE
|
||||
&& scope->se_type != FOR_SCOPE)
|
||||
scope = scope->se_outer;
|
||||
if (scope == NULL)
|
||||
return 0;
|
||||
|
||||
if (scope->se_type == WHILE_SCOPE)
|
||||
start_local_count = scope->se_u.se_while.ws_local_count;
|
||||
else
|
||||
start_local_count = scope->se_u.se_for.fs_local_count;
|
||||
if (cctx->ctx_locals.ga_len > start_local_count)
|
||||
{
|
||||
*loop_var_idx = (short)start_local_count;
|
||||
return (short)(cctx->ctx_locals.ga_len - start_local_count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the index of the first variable in a loop, if any.
|
||||
* Returns -1 if none.
|
||||
*/
|
||||
int
|
||||
get_loop_var_idx(cctx_T *cctx)
|
||||
{
|
||||
short loop_var_idx;
|
||||
|
||||
if (get_loop_var_info(cctx, &loop_var_idx) > 0)
|
||||
return loop_var_idx;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* compile "continue"
|
||||
*/
|
||||
|
@@ -54,6 +54,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
|
||||
{
|
||||
*lvar = *lvp;
|
||||
lvar->lv_from_outer = 0;
|
||||
lvar->lv_loop_idx = get_loop_var_idx(cctx);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
@@ -954,7 +955,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
|
||||
// recursive call.
|
||||
if (is_global)
|
||||
{
|
||||
r = generate_NEWFUNC(cctx, lambda_name, func_name);
|
||||
// TODO: loop variable index and count
|
||||
r = generate_NEWFUNC(cctx, lambda_name, func_name, 0, 0);
|
||||
func_name = NULL;
|
||||
lambda_name = NULL;
|
||||
}
|
||||
@@ -1193,7 +1195,7 @@ generate_loadvar(
|
||||
{
|
||||
if (lvar->lv_from_outer > 0)
|
||||
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
|
||||
type);
|
||||
lvar->lv_loop_idx, type);
|
||||
else
|
||||
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
|
||||
}
|
||||
|
@@ -673,6 +673,9 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
|
||||
if (closure_count == 0)
|
||||
return OK; // no funcrefs created
|
||||
|
||||
// Compute "top": the first entry in the stack used by the function.
|
||||
// This is the first argument (after that comes the stack frame and then
|
||||
// the local variables).
|
||||
argcount = ufunc_argcount(dfunc->df_ufunc);
|
||||
top = ectx->ec_frame_idx - argcount;
|
||||
|
||||
@@ -740,6 +743,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
|
||||
else
|
||||
copy_tv(tv, stack + idx);
|
||||
}
|
||||
// Skip the stack frame.
|
||||
// Move the local variables.
|
||||
for (idx = 0; idx < dfunc->df_varcount; ++idx)
|
||||
{
|
||||
@@ -770,10 +774,17 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
|
||||
- closure_count + idx];
|
||||
if (pt->pt_refcount > 1)
|
||||
{
|
||||
int prev_frame_idx = pt->pt_outer.out_frame_idx;
|
||||
|
||||
++funcstack->fs_refcount;
|
||||
pt->pt_funcstack = funcstack;
|
||||
pt->pt_outer.out_stack = &funcstack->fs_ga;
|
||||
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx - top;
|
||||
|
||||
// TODO: drop this, should be done at ISN_ENDLOOP
|
||||
pt->pt_outer.out_loop_stack = &funcstack->fs_ga;
|
||||
pt->pt_outer.out_loop_var_idx -=
|
||||
prev_frame_idx - pt->pt_outer.out_frame_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1814,7 +1825,12 @@ call_eval_func(
|
||||
* needed, especially when it is used as a closure.
|
||||
*/
|
||||
int
|
||||
fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
|
||||
fill_partial_and_closure(
|
||||
partial_T *pt,
|
||||
ufunc_T *ufunc,
|
||||
short loop_var_idx,
|
||||
short loop_var_count,
|
||||
ectx_T *ectx)
|
||||
{
|
||||
pt->pt_func = ufunc;
|
||||
pt->pt_refcount = 1;
|
||||
@@ -1839,6 +1855,14 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
|
||||
}
|
||||
}
|
||||
|
||||
// The closure may need to find variables defined inside a loop. A
|
||||
// new reference is made every time, ISN_ENDLOOP will check if they
|
||||
// are actually used.
|
||||
pt->pt_outer.out_loop_stack = &ectx->ec_stack;
|
||||
pt->pt_outer.out_loop_var_idx = ectx->ec_frame_idx + STACK_FRAME_SIZE
|
||||
+ loop_var_idx;
|
||||
pt->pt_outer.out_loop_var_count = loop_var_count;
|
||||
|
||||
// If the function currently executing returns and the closure is still
|
||||
// being referenced, we need to make a copy of the context (arguments
|
||||
// and local variables) so that the closure can use it later.
|
||||
@@ -1853,8 +1877,8 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
|
||||
++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx
|
||||
+ STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number;
|
||||
|
||||
((partial_T **)ectx->ec_funcrefs.ga_data)
|
||||
[ectx->ec_funcrefs.ga_len] = pt;
|
||||
((partial_T **)ectx->ec_funcrefs.ga_data)[ectx->ec_funcrefs.ga_len]
|
||||
= pt;
|
||||
++pt->pt_refcount;
|
||||
++ectx->ec_funcrefs.ga_len;
|
||||
}
|
||||
@@ -3610,9 +3634,15 @@ exec_instructions(ectx_T *ectx)
|
||||
iemsg("LOADOUTER depth more than scope levels");
|
||||
goto theend;
|
||||
}
|
||||
tv = ((typval_T *)outer->out_stack->ga_data)
|
||||
+ outer->out_frame_idx + STACK_FRAME_SIZE
|
||||
+ iptr->isn_arg.outer.outer_idx;
|
||||
if (depth == OUTER_LOOP_DEPTH)
|
||||
// variable declared in loop
|
||||
tv = ((typval_T *)outer->out_loop_stack->ga_data)
|
||||
+ outer->out_loop_var_idx
|
||||
+ iptr->isn_arg.outer.outer_idx;
|
||||
else
|
||||
tv = ((typval_T *)outer->out_stack->ga_data)
|
||||
+ outer->out_frame_idx + STACK_FRAME_SIZE
|
||||
+ iptr->isn_arg.outer.outer_idx;
|
||||
if (iptr->isn_type == ISN_LOADOUTER)
|
||||
{
|
||||
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
@@ -3913,9 +3943,10 @@ exec_instructions(ectx_T *ectx)
|
||||
// push a partial, a reference to a compiled function
|
||||
case ISN_FUNCREF:
|
||||
{
|
||||
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
|
||||
ufunc_T *ufunc;
|
||||
funcref_T *funcref = &iptr->isn_arg.funcref;
|
||||
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
|
||||
ufunc_T *ufunc;
|
||||
funcref_T *funcref = &iptr->isn_arg.funcref;
|
||||
funcref_extra_T *extra = funcref->fr_extra;
|
||||
|
||||
if (pt == NULL)
|
||||
goto theend;
|
||||
@@ -3924,7 +3955,7 @@ exec_instructions(ectx_T *ectx)
|
||||
vim_free(pt);
|
||||
goto theend;
|
||||
}
|
||||
if (funcref->fr_func_name == NULL)
|
||||
if (extra == NULL || extra->fre_func_name == NULL)
|
||||
{
|
||||
dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
|
||||
+ funcref->fr_dfunc_idx;
|
||||
@@ -3932,16 +3963,17 @@ exec_instructions(ectx_T *ectx)
|
||||
ufunc = pt_dfunc->df_ufunc;
|
||||
}
|
||||
else
|
||||
{
|
||||
ufunc = find_func(funcref->fr_func_name, FALSE);
|
||||
}
|
||||
ufunc = find_func(extra->fre_func_name, FALSE);
|
||||
if (ufunc == NULL)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
iemsg("ufunc unexpectedly NULL for FUNCREF");
|
||||
goto theend;
|
||||
}
|
||||
if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
|
||||
if (fill_partial_and_closure(pt, ufunc,
|
||||
extra == NULL ? 0 : extra->fre_loop_var_idx,
|
||||
extra == NULL ? 0 : extra->fre_loop_var_count,
|
||||
ectx) == FAIL)
|
||||
goto theend;
|
||||
tv = STACK_TV_BOT(0);
|
||||
++ectx->ec_stack.ga_len;
|
||||
@@ -3954,10 +3986,11 @@ exec_instructions(ectx_T *ectx)
|
||||
// Create a global function from a lambda.
|
||||
case ISN_NEWFUNC:
|
||||
{
|
||||
newfunc_T *newfunc = &iptr->isn_arg.newfunc;
|
||||
newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg;
|
||||
|
||||
if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
|
||||
ectx) == FAIL)
|
||||
if (copy_lambda_to_global_func(arg->nfa_lambda,
|
||||
arg->nfa_global, arg->nfa_loop_var_idx,
|
||||
arg->nfa_loop_var_count, ectx) == FAIL)
|
||||
goto theend;
|
||||
}
|
||||
break;
|
||||
@@ -5520,7 +5553,7 @@ call_def_function(
|
||||
ufunc_T *base_ufunc = dfunc->df_ufunc;
|
||||
|
||||
// "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
|
||||
// by copy_func().
|
||||
// by copy_lambda_to_global_func().
|
||||
if (partial != NULL || base_ufunc->uf_partial != NULL)
|
||||
{
|
||||
ectx.ec_outer_ref = ALLOC_CLEAR_ONE(outer_ref_T);
|
||||
@@ -5880,15 +5913,20 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
break;
|
||||
case ISN_LOADOUTER:
|
||||
{
|
||||
if (iptr->isn_arg.outer.outer_idx < 0)
|
||||
isn_outer_T *outer = &iptr->isn_arg.outer;
|
||||
|
||||
if (outer->outer_idx < 0)
|
||||
smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
|
||||
iptr->isn_arg.outer.outer_depth,
|
||||
iptr->isn_arg.outer.outer_idx
|
||||
outer->outer_depth,
|
||||
outer->outer_idx
|
||||
+ STACK_FRAME_SIZE);
|
||||
else if (outer->outer_depth == OUTER_LOOP_DEPTH)
|
||||
smsg("%s%4d LOADOUTER level 1 $%d in loop",
|
||||
pfx, current, outer->outer_idx);
|
||||
else
|
||||
smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
|
||||
iptr->isn_arg.outer.outer_depth,
|
||||
iptr->isn_arg.outer.outer_idx);
|
||||
outer->outer_depth,
|
||||
outer->outer_idx);
|
||||
}
|
||||
break;
|
||||
case ISN_LOADV:
|
||||
@@ -5971,9 +6009,16 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
iptr->isn_arg.number);
|
||||
break;
|
||||
case ISN_STOREOUTER:
|
||||
smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
|
||||
iptr->isn_arg.outer.outer_depth,
|
||||
iptr->isn_arg.outer.outer_idx);
|
||||
{
|
||||
isn_outer_T *outer = &iptr->isn_arg.outer;
|
||||
|
||||
if (outer->outer_depth == OUTER_LOOP_DEPTH)
|
||||
smsg("%s%4d STOREOUTER level 1 $%d in loop",
|
||||
pfx, current, outer->outer_idx);
|
||||
else
|
||||
smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
|
||||
outer->outer_depth, outer->outer_idx);
|
||||
}
|
||||
break;
|
||||
case ISN_STOREV:
|
||||
smsg("%s%4d STOREV v:%s", pfx, current,
|
||||
@@ -6190,27 +6235,41 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
break;
|
||||
case ISN_FUNCREF:
|
||||
{
|
||||
funcref_T *funcref = &iptr->isn_arg.funcref;
|
||||
char_u *name;
|
||||
funcref_T *funcref = &iptr->isn_arg.funcref;
|
||||
funcref_extra_T *extra = funcref->fr_extra;
|
||||
char_u *name;
|
||||
|
||||
if (funcref->fr_func_name == NULL)
|
||||
if (extra == NULL || extra->fre_func_name == NULL)
|
||||
{
|
||||
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
|
||||
+ funcref->fr_dfunc_idx;
|
||||
name = df->df_ufunc->uf_name;
|
||||
}
|
||||
else
|
||||
name = funcref->fr_func_name;
|
||||
smsg("%s%4d FUNCREF %s", pfx, current, name);
|
||||
name = extra->fre_func_name;
|
||||
if (extra == NULL || extra->fre_loop_var_count == 0)
|
||||
smsg("%s%4d FUNCREF %s", pfx, current, name);
|
||||
else
|
||||
smsg("%s%4d FUNCREF %s var $%d - $%d", pfx, current,
|
||||
name,
|
||||
extra->fre_loop_var_idx,
|
||||
extra->fre_loop_var_idx
|
||||
+ extra->fre_loop_var_count - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case ISN_NEWFUNC:
|
||||
{
|
||||
newfunc_T *newfunc = &iptr->isn_arg.newfunc;
|
||||
newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg;
|
||||
|
||||
smsg("%s%4d NEWFUNC %s %s", pfx, current,
|
||||
newfunc->nf_lambda, newfunc->nf_global);
|
||||
if (arg->nfa_loop_var_count == 0)
|
||||
smsg("%s%4d NEWFUNC %s %s", pfx, current,
|
||||
arg->nfa_lambda, arg->nfa_global);
|
||||
else
|
||||
smsg("%s%4d NEWFUNC %s %s var $%d - $%d", pfx, current,
|
||||
arg->nfa_lambda, arg->nfa_global,
|
||||
arg->nfa_loop_var_idx,
|
||||
arg->nfa_loop_var_idx + arg->nfa_loop_var_count - 1);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@@ -496,6 +496,7 @@ compile_load(
|
||||
int idx;
|
||||
int gen_load = FALSE;
|
||||
int gen_load_outer = 0;
|
||||
int outer_loop_idx = -1;
|
||||
|
||||
name = vim_strnsave(*arg, end - *arg);
|
||||
if (name == NULL)
|
||||
@@ -520,6 +521,7 @@ compile_load(
|
||||
{
|
||||
type = lvar.lv_type;
|
||||
idx = lvar.lv_idx;
|
||||
outer_loop_idx = lvar.lv_loop_idx;
|
||||
if (lvar.lv_from_outer != 0)
|
||||
gen_load_outer = lvar.lv_from_outer;
|
||||
else
|
||||
@@ -544,7 +546,8 @@ compile_load(
|
||||
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
|
||||
if (gen_load_outer > 0)
|
||||
{
|
||||
res = generate_LOADOUTER(cctx, idx, gen_load_outer, type);
|
||||
res = generate_LOADOUTER(cctx, idx,
|
||||
gen_load_outer, outer_loop_idx, type);
|
||||
cctx->ctx_outer_used = TRUE;
|
||||
}
|
||||
}
|
||||
|
121
src/vim9instr.c
121
src/vim9instr.c
@@ -916,15 +916,25 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
|
||||
* Generate an ISN_STOREOUTER instruction.
|
||||
*/
|
||||
static int
|
||||
generate_STOREOUTER(cctx_T *cctx, int idx, int level)
|
||||
generate_STOREOUTER(cctx_T *cctx, int idx, int level, int loop_idx)
|
||||
{
|
||||
isn_T *isn;
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.outer.outer_idx = idx;
|
||||
isn->isn_arg.outer.outer_depth = level;
|
||||
if (level == 1 && loop_idx >= 0 && idx >= loop_idx)
|
||||
{
|
||||
// Store a variable defined in a loop. A copy will be made at the end
|
||||
// of the loop. TODO: how about deeper nesting?
|
||||
isn->isn_arg.outer.outer_idx = idx - loop_idx;
|
||||
isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
isn->isn_arg.outer.outer_idx = idx;
|
||||
isn->isn_arg.outer.outer_depth = level;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -999,6 +1009,7 @@ generate_LOADOUTER(
|
||||
cctx_T *cctx,
|
||||
int idx,
|
||||
int nesting,
|
||||
int loop_idx,
|
||||
type_T *type)
|
||||
{
|
||||
isn_T *isn;
|
||||
@@ -1006,8 +1017,18 @@ generate_LOADOUTER(
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.outer.outer_idx = idx;
|
||||
isn->isn_arg.outer.outer_depth = nesting;
|
||||
if (nesting == 1 && loop_idx >= 0 && idx >= loop_idx)
|
||||
{
|
||||
// Load a variable defined in a loop. A copy will be made at the end
|
||||
// of the loop. TODO: how about deeper nesting?
|
||||
isn->isn_arg.outer.outer_idx = idx - loop_idx;
|
||||
isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
isn->isn_arg.outer.outer_idx = idx;
|
||||
isn->isn_arg.outer.outer_depth = nesting;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -1186,20 +1207,39 @@ generate_NEWDICT(cctx_T *cctx, int count, int use_null)
|
||||
/*
|
||||
* Generate an ISN_FUNCREF instruction.
|
||||
* "isnp" is set to the instruction, so that fr_dfunc_idx can be set later.
|
||||
* If variables were declared inside a loop "loop_var_idx" is the index of the
|
||||
* first one and "loop_var_count" the number of variables declared.
|
||||
*/
|
||||
int
|
||||
generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
|
||||
generate_FUNCREF(
|
||||
cctx_T *cctx,
|
||||
ufunc_T *ufunc,
|
||||
isn_T **isnp)
|
||||
{
|
||||
isn_T *isn;
|
||||
type_T *type;
|
||||
isn_T *isn;
|
||||
type_T *type;
|
||||
funcref_extra_T *extra;
|
||||
short loop_var_idx;
|
||||
short loop_var_count;
|
||||
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
|
||||
return FAIL;
|
||||
if (isnp != NULL)
|
||||
*isnp = isn;
|
||||
|
||||
loop_var_count = get_loop_var_info(cctx, &loop_var_idx);
|
||||
if (ufunc->uf_def_status == UF_NOT_COMPILED || loop_var_count > 0)
|
||||
{
|
||||
extra = ALLOC_CLEAR_ONE(funcref_extra_T);
|
||||
if (extra == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.funcref.fr_extra = extra;
|
||||
extra->fre_loop_var_idx = loop_var_idx;
|
||||
extra->fre_loop_var_count = loop_var_count;
|
||||
}
|
||||
if (ufunc->uf_def_status == UF_NOT_COMPILED)
|
||||
isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name);
|
||||
extra->fre_func_name = vim_strsave(ufunc->uf_name);
|
||||
else
|
||||
isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
|
||||
cctx->ctx_has_closure = 1;
|
||||
@@ -1221,7 +1261,12 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
|
||||
* consumed.
|
||||
*/
|
||||
int
|
||||
generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
|
||||
generate_NEWFUNC(
|
||||
cctx_T *cctx,
|
||||
char_u *lambda_name,
|
||||
char_u *func_name,
|
||||
short loop_var_idx,
|
||||
short loop_var_count)
|
||||
{
|
||||
isn_T *isn;
|
||||
int ret = OK;
|
||||
@@ -1232,9 +1277,19 @@ generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
|
||||
ret = FAIL;
|
||||
else
|
||||
{
|
||||
isn->isn_arg.newfunc.nf_lambda = lambda_name;
|
||||
isn->isn_arg.newfunc.nf_global = func_name;
|
||||
return OK;
|
||||
newfuncarg_T *arg = ALLOC_CLEAR_ONE(newfuncarg_T);
|
||||
|
||||
if (arg == NULL)
|
||||
ret = FAIL;
|
||||
else
|
||||
{
|
||||
isn->isn_arg.newfunc.nf_arg = arg;
|
||||
arg->nfa_lambda = lambda_name;
|
||||
arg->nfa_global = func_name;
|
||||
arg->nfa_loop_var_idx = loop_var_idx;
|
||||
arg->nfa_loop_var_count = loop_var_count;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
vim_free(lambda_name);
|
||||
@@ -2123,7 +2178,7 @@ generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl)
|
||||
}
|
||||
else if (lhs->lhs_lvar->lv_from_outer > 0)
|
||||
generate_STOREOUTER(cctx, lhs->lhs_lvar->lv_idx,
|
||||
lhs->lhs_lvar->lv_from_outer);
|
||||
lhs->lhs_lvar->lv_from_outer, lhs->lhs_lvar->lv_loop_idx);
|
||||
else
|
||||
generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL);
|
||||
}
|
||||
@@ -2226,22 +2281,28 @@ delete_instr(isn_T *isn)
|
||||
|
||||
case ISN_FUNCREF:
|
||||
{
|
||||
if (isn->isn_arg.funcref.fr_func_name == NULL)
|
||||
funcref_T *funcref = &isn->isn_arg.funcref;
|
||||
funcref_extra_T *extra = funcref->fr_extra;
|
||||
|
||||
if (extra == NULL || extra->fre_func_name == NULL)
|
||||
{
|
||||
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
|
||||
+ isn->isn_arg.funcref.fr_dfunc_idx;
|
||||
+ funcref->fr_dfunc_idx;
|
||||
ufunc_T *ufunc = dfunc->df_ufunc;
|
||||
|
||||
if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
|
||||
func_ptr_unref(ufunc);
|
||||
}
|
||||
else
|
||||
if (extra != NULL)
|
||||
{
|
||||
char_u *name = isn->isn_arg.funcref.fr_func_name;
|
||||
char_u *name = extra->fre_func_name;
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
func_unref(name);
|
||||
vim_free(isn->isn_arg.funcref.fr_func_name);
|
||||
vim_free(name);
|
||||
}
|
||||
vim_free(extra);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2259,17 +2320,23 @@ delete_instr(isn_T *isn)
|
||||
|
||||
case ISN_NEWFUNC:
|
||||
{
|
||||
char_u *lambda = isn->isn_arg.newfunc.nf_lambda;
|
||||
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
|
||||
newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
|
||||
|
||||
if (ufunc != NULL)
|
||||
if (arg != NULL)
|
||||
{
|
||||
unlink_def_function(ufunc);
|
||||
func_ptr_unref(ufunc);
|
||||
}
|
||||
ufunc_T *ufunc = find_func_even_dead(
|
||||
arg->nfa_lambda, FFED_IS_GLOBAL);
|
||||
|
||||
vim_free(lambda);
|
||||
vim_free(isn->isn_arg.newfunc.nf_global);
|
||||
if (ufunc != NULL)
|
||||
{
|
||||
unlink_def_function(ufunc);
|
||||
func_ptr_unref(ufunc);
|
||||
}
|
||||
|
||||
vim_free(arg->nfa_lambda);
|
||||
vim_free(arg->nfa_global);
|
||||
vim_free(arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
Reference in New Issue
Block a user