1
0
forked from aniani/vim

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:
Bram Moolenaar
2022-09-16 19:04:24 +01:00
parent abd58d8aee
commit 8fa745e7be
13 changed files with 285 additions and 77 deletions

View File

@@ -16,7 +16,7 @@ int func_is_global(ufunc_T *ufunc);
int func_requires_g_prefix(ufunc_T *ufunc); int func_requires_g_prefix(ufunc_T *ufunc);
int func_name_refcount(char_u *name); int func_name_refcount(char_u *name);
void func_clear_free(ufunc_T *fp, int force); 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); int funcdepth_increment(void);
void funcdepth_decrement(void); void funcdepth_decrement(void);
int funcdepth_get(void); int funcdepth_get(void);

View File

@@ -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_endfor(char_u *arg, cctx_T *cctx);
char_u *compile_while(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); 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_continue(char_u *arg, cctx_T *cctx);
char_u *compile_break(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); char_u *compile_block(char_u *arg, cctx_T *cctx);

View File

@@ -9,7 +9,7 @@ void restore_current_ectx(ectx_T *ectx);
int add_defer_function(char_u *name, int argcount, typval_T *argvars); 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 *char_from_string(char_u *str, varnumber_T index);
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive); 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); int may_load_script(int sid, int *loaded);
typval_T *lookup_debug_var(char_u *name); typval_T *lookup_debug_var(char_u *name);
int may_break_in_function(ufunc_T *ufunc); int may_break_in_function(ufunc_T *ufunc);

View File

@@ -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_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_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_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_LOADV(cctx_T *cctx, char_u *name);
int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit); int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
int generate_LOCKCONST(cctx_T *cctx); 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_NEWLIST(cctx_T *cctx, int count, int use_null);
int generate_NEWDICT(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_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_DEF(cctx_T *cctx, char_u *name, size_t len);
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where); int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
int generate_WHILE(cctx_T *cctx, int funcref_idx); int generate_WHILE(cctx_T *cctx, int funcref_idx);

View File

@@ -1656,7 +1656,7 @@ typedef enum {
/* /*
* Structure to hold info for a user function. * 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 typedef struct
{ {
@@ -1741,7 +1741,8 @@ typedef struct
#define FC_NOARGS 0x200 // no a: variables in lambda #define FC_NOARGS 0x200 // no a: variables in lambda
#define FC_VIM9 0x400 // defined in vim9 script file #define FC_VIM9 0x400 // defined in vim9 script file
#define FC_CFUNC 0x800 // defined as Lua C func #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 FC_LAMBDA 0x2000 // one line "return {expr}"
#define MAX_FUNC_ARGS 20 // maximum number of function arguments #define MAX_FUNC_ARGS 20 // maximum number of function arguments
@@ -2096,10 +2097,17 @@ struct funcstack_S
typedef struct outer_S outer_T; typedef struct outer_S outer_T;
struct outer_S { 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 int out_frame_idx; // index of stack frame in out_stack
outer_T *out_up; // outer scope of outer scope or NULL outer_T *out_up; // outer scope of outer scope or NULL
partial_T *out_up_partial; // partial owning out_up 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 struct partial_S

View File

@@ -2452,7 +2452,12 @@ func_clear_free(ufunc_T *fp, int force)
* This is for when a compiled function defines a global function. * This is for when a compiled function defines a global function.
*/ */
int 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 *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
ufunc_T *fp = NULL; ufunc_T *fp = NULL;
@@ -2519,7 +2524,8 @@ copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
if (pt == NULL) if (pt == NULL)
goto failed; 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); vim_free(pt);
goto failed; goto failed;

View File

@@ -703,6 +703,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 */
/**/
481,
/**/ /**/
480, 480,
/**/ /**/

View File

@@ -354,16 +354,29 @@ typedef struct {
int ul_forceit; // forceit flag int ul_forceit; // forceit flag
} unlet_T; } 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 // arguments to ISN_FUNCREF
typedef struct { typedef struct {
int fr_dfunc_idx; // function index for :def function int fr_dfunc_idx; // function index for :def function
char_u *fr_func_name; // function name for legacy function funcref_extra_T *fr_extra; // optional extra information
} funcref_T; } funcref_T;
// arguments to ISN_NEWFUNC // arguments to ISN_NEWFUNC
typedef struct { typedef struct {
char_u *nf_lambda; // name of the lambda already defined char_u *nfa_lambda; // name of the lambda already defined
char_u *nf_global; // name of the global function to be created 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; } newfunc_T;
// arguments to ISN_CHECKLEN // arguments to ISN_CHECKLEN
@@ -401,6 +414,8 @@ typedef struct {
int outer_depth; // nesting level, stack frames to go up int outer_depth; // nesting level, stack frames to go up
} isn_outer_T; } isn_outer_T;
#define OUTER_LOOP_DEPTH -9 // used for outer_depth for loop variables
// arguments to ISN_SUBSTITUTE // arguments to ISN_SUBSTITUTE
typedef struct { typedef struct {
char_u *subs_cmd; // :s command char_u *subs_cmd; // :s command
@@ -677,6 +692,7 @@ typedef struct {
char_u *lv_name; char_u *lv_name;
type_T *lv_type; type_T *lv_type;
int lv_idx; // index of the variable on the stack 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_from_outer; // nesting level, using ctx_outer scope
int lv_const; // when TRUE cannot be assigned to int lv_const; // when TRUE cannot be assigned to
int lv_arg; // when TRUE this is an argument int lv_arg; // when TRUE this is an argument

View File

@@ -1245,6 +1245,49 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
return arg; 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" * compile "continue"
*/ */

View File

@@ -54,6 +54,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
{ {
*lvar = *lvp; *lvar = *lvp;
lvar->lv_from_outer = 0; lvar->lv_from_outer = 0;
lvar->lv_loop_idx = get_loop_var_idx(cctx);
} }
return OK; return OK;
} }
@@ -954,7 +955,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
// recursive call. // recursive call.
if (is_global) 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; func_name = NULL;
lambda_name = NULL; lambda_name = NULL;
} }
@@ -1193,7 +1195,7 @@ generate_loadvar(
{ {
if (lvar->lv_from_outer > 0) if (lvar->lv_from_outer > 0)
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer, generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
type); lvar->lv_loop_idx, type);
else else
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type); generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
} }

View File

@@ -673,6 +673,9 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
if (closure_count == 0) if (closure_count == 0)
return OK; // no funcrefs created 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); argcount = ufunc_argcount(dfunc->df_ufunc);
top = ectx->ec_frame_idx - argcount; top = ectx->ec_frame_idx - argcount;
@@ -740,6 +743,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
else else
copy_tv(tv, stack + idx); copy_tv(tv, stack + idx);
} }
// Skip the stack frame.
// Move the local variables. // Move the local variables.
for (idx = 0; idx < dfunc->df_varcount; ++idx) 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]; - closure_count + idx];
if (pt->pt_refcount > 1) if (pt->pt_refcount > 1)
{ {
int prev_frame_idx = pt->pt_outer.out_frame_idx;
++funcstack->fs_refcount; ++funcstack->fs_refcount;
pt->pt_funcstack = funcstack; pt->pt_funcstack = funcstack;
pt->pt_outer.out_stack = &funcstack->fs_ga; pt->pt_outer.out_stack = &funcstack->fs_ga;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx - top; 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. * needed, especially when it is used as a closure.
*/ */
int 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_func = ufunc;
pt->pt_refcount = 1; 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 // If the function currently executing returns and the closure is still
// being referenced, we need to make a copy of the context (arguments // being referenced, we need to make a copy of the context (arguments
// and local variables) so that the closure can use it later. // 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 ++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx
+ STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number; + STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number;
((partial_T **)ectx->ec_funcrefs.ga_data) ((partial_T **)ectx->ec_funcrefs.ga_data)[ectx->ec_funcrefs.ga_len]
[ectx->ec_funcrefs.ga_len] = pt; = pt;
++pt->pt_refcount; ++pt->pt_refcount;
++ectx->ec_funcrefs.ga_len; ++ectx->ec_funcrefs.ga_len;
} }
@@ -3610,9 +3634,15 @@ exec_instructions(ectx_T *ectx)
iemsg("LOADOUTER depth more than scope levels"); iemsg("LOADOUTER depth more than scope levels");
goto theend; goto theend;
} }
tv = ((typval_T *)outer->out_stack->ga_data) if (depth == OUTER_LOOP_DEPTH)
+ outer->out_frame_idx + STACK_FRAME_SIZE // variable declared in loop
+ iptr->isn_arg.outer.outer_idx; 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 (iptr->isn_type == ISN_LOADOUTER)
{ {
if (GA_GROW_FAILS(&ectx->ec_stack, 1)) 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 // push a partial, a reference to a compiled function
case ISN_FUNCREF: case ISN_FUNCREF:
{ {
partial_T *pt = ALLOC_CLEAR_ONE(partial_T); partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
ufunc_T *ufunc; ufunc_T *ufunc;
funcref_T *funcref = &iptr->isn_arg.funcref; funcref_T *funcref = &iptr->isn_arg.funcref;
funcref_extra_T *extra = funcref->fr_extra;
if (pt == NULL) if (pt == NULL)
goto theend; goto theend;
@@ -3924,7 +3955,7 @@ exec_instructions(ectx_T *ectx)
vim_free(pt); vim_free(pt);
goto theend; 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) dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx; + funcref->fr_dfunc_idx;
@@ -3932,16 +3963,17 @@ exec_instructions(ectx_T *ectx)
ufunc = pt_dfunc->df_ufunc; ufunc = pt_dfunc->df_ufunc;
} }
else else
{ ufunc = find_func(extra->fre_func_name, FALSE);
ufunc = find_func(funcref->fr_func_name, FALSE);
}
if (ufunc == NULL) if (ufunc == NULL)
{ {
SOURCING_LNUM = iptr->isn_lnum; SOURCING_LNUM = iptr->isn_lnum;
iemsg("ufunc unexpectedly NULL for FUNCREF"); iemsg("ufunc unexpectedly NULL for FUNCREF");
goto theend; 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; goto theend;
tv = STACK_TV_BOT(0); tv = STACK_TV_BOT(0);
++ectx->ec_stack.ga_len; ++ectx->ec_stack.ga_len;
@@ -3954,10 +3986,11 @@ exec_instructions(ectx_T *ectx)
// Create a global function from a lambda. // Create a global function from a lambda.
case ISN_NEWFUNC: 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, if (copy_lambda_to_global_func(arg->nfa_lambda,
ectx) == FAIL) arg->nfa_global, arg->nfa_loop_var_idx,
arg->nfa_loop_var_count, ectx) == FAIL)
goto theend; goto theend;
} }
break; break;
@@ -5520,7 +5553,7 @@ call_def_function(
ufunc_T *base_ufunc = dfunc->df_ufunc; ufunc_T *base_ufunc = dfunc->df_ufunc;
// "uf_partial" is on the ufunc that "df_ufunc" points to, as is done // "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) if (partial != NULL || base_ufunc->uf_partial != NULL)
{ {
ectx.ec_outer_ref = ALLOC_CLEAR_ONE(outer_ref_T); 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; break;
case ISN_LOADOUTER: 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, smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
iptr->isn_arg.outer.outer_depth, outer->outer_depth,
iptr->isn_arg.outer.outer_idx outer->outer_idx
+ STACK_FRAME_SIZE); + 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 else
smsg("%s%4d LOADOUTER level %d $%d", pfx, current, smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
iptr->isn_arg.outer.outer_depth, outer->outer_depth,
iptr->isn_arg.outer.outer_idx); outer->outer_idx);
} }
break; break;
case ISN_LOADV: case ISN_LOADV:
@@ -5971,9 +6009,16 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
iptr->isn_arg.number); iptr->isn_arg.number);
break; break;
case ISN_STOREOUTER: case ISN_STOREOUTER:
smsg("%s%4d STOREOUTER level %d $%d", pfx, current, {
iptr->isn_arg.outer.outer_depth, isn_outer_T *outer = &iptr->isn_arg.outer;
iptr->isn_arg.outer.outer_idx);
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; break;
case ISN_STOREV: case ISN_STOREV:
smsg("%s%4d STOREV v:%s", pfx, current, 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; break;
case ISN_FUNCREF: case ISN_FUNCREF:
{ {
funcref_T *funcref = &iptr->isn_arg.funcref; funcref_T *funcref = &iptr->isn_arg.funcref;
char_u *name; 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) dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx; + funcref->fr_dfunc_idx;
name = df->df_ufunc->uf_name; name = df->df_ufunc->uf_name;
} }
else else
name = funcref->fr_func_name; name = extra->fre_func_name;
smsg("%s%4d FUNCREF %s", pfx, current, 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; break;
case ISN_NEWFUNC: 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, if (arg->nfa_loop_var_count == 0)
newfunc->nf_lambda, newfunc->nf_global); 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; break;

View File

@@ -496,6 +496,7 @@ compile_load(
int idx; int idx;
int gen_load = FALSE; int gen_load = FALSE;
int gen_load_outer = 0; int gen_load_outer = 0;
int outer_loop_idx = -1;
name = vim_strnsave(*arg, end - *arg); name = vim_strnsave(*arg, end - *arg);
if (name == NULL) if (name == NULL)
@@ -520,6 +521,7 @@ compile_load(
{ {
type = lvar.lv_type; type = lvar.lv_type;
idx = lvar.lv_idx; idx = lvar.lv_idx;
outer_loop_idx = lvar.lv_loop_idx;
if (lvar.lv_from_outer != 0) if (lvar.lv_from_outer != 0)
gen_load_outer = lvar.lv_from_outer; gen_load_outer = lvar.lv_from_outer;
else else
@@ -544,7 +546,8 @@ compile_load(
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
if (gen_load_outer > 0) 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; cctx->ctx_outer_used = TRUE;
} }
} }

View File

@@ -916,15 +916,25 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
* Generate an ISN_STOREOUTER instruction. * Generate an ISN_STOREOUTER instruction.
*/ */
static int 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; isn_T *isn;
RETURN_OK_IF_SKIP(cctx); RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL) if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL)
return FAIL; return FAIL;
isn->isn_arg.outer.outer_idx = idx; if (level == 1 && loop_idx >= 0 && idx >= loop_idx)
isn->isn_arg.outer.outer_depth = level; {
// 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; return OK;
} }
@@ -999,6 +1009,7 @@ generate_LOADOUTER(
cctx_T *cctx, cctx_T *cctx,
int idx, int idx,
int nesting, int nesting,
int loop_idx,
type_T *type) type_T *type)
{ {
isn_T *isn; isn_T *isn;
@@ -1006,8 +1017,18 @@ generate_LOADOUTER(
RETURN_OK_IF_SKIP(cctx); RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL) if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL)
return FAIL; return FAIL;
isn->isn_arg.outer.outer_idx = idx; if (nesting == 1 && loop_idx >= 0 && idx >= loop_idx)
isn->isn_arg.outer.outer_depth = nesting; {
// 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; return OK;
} }
@@ -1186,20 +1207,39 @@ generate_NEWDICT(cctx_T *cctx, int count, int use_null)
/* /*
* Generate an ISN_FUNCREF instruction. * Generate an ISN_FUNCREF instruction.
* "isnp" is set to the instruction, so that fr_dfunc_idx can be set later. * "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 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; isn_T *isn;
type_T *type; type_T *type;
funcref_extra_T *extra;
short loop_var_idx;
short loop_var_count;
RETURN_OK_IF_SKIP(cctx); RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL) if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
return FAIL; return FAIL;
if (isnp != NULL) if (isnp != NULL)
*isnp = isn; *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) 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 else
isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx; isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
cctx->ctx_has_closure = 1; cctx->ctx_has_closure = 1;
@@ -1221,7 +1261,12 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
* consumed. * consumed.
*/ */
int 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; isn_T *isn;
int ret = OK; int ret = OK;
@@ -1232,9 +1277,19 @@ generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
ret = FAIL; ret = FAIL;
else else
{ {
isn->isn_arg.newfunc.nf_lambda = lambda_name; newfuncarg_T *arg = ALLOC_CLEAR_ONE(newfuncarg_T);
isn->isn_arg.newfunc.nf_global = func_name;
return OK; 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); 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) else if (lhs->lhs_lvar->lv_from_outer > 0)
generate_STOREOUTER(cctx, lhs->lhs_lvar->lv_idx, 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 else
generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL); generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL);
} }
@@ -2226,22 +2281,28 @@ delete_instr(isn_T *isn)
case ISN_FUNCREF: 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) 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; ufunc_T *ufunc = dfunc->df_ufunc;
if (ufunc != NULL && func_name_refcount(ufunc->uf_name)) if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
func_ptr_unref(ufunc); 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) if (name != NULL)
{
func_unref(name); func_unref(name);
vim_free(isn->isn_arg.funcref.fr_func_name); vim_free(name);
}
vim_free(extra);
} }
} }
break; break;
@@ -2259,17 +2320,23 @@ delete_instr(isn_T *isn)
case ISN_NEWFUNC: case ISN_NEWFUNC:
{ {
char_u *lambda = isn->isn_arg.newfunc.nf_lambda; newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
if (ufunc != NULL) if (arg != NULL)
{ {
unlink_def_function(ufunc); ufunc_T *ufunc = find_func_even_dead(
func_ptr_unref(ufunc); arg->nfa_lambda, FFED_IS_GLOBAL);
}
vim_free(lambda); if (ufunc != NULL)
vim_free(isn->isn_arg.newfunc.nf_global); {
unlink_def_function(ufunc);
func_ptr_unref(ufunc);
}
vim_free(arg->nfa_lambda);
vim_free(arg->nfa_global);
vim_free(arg);
}
} }
break; break;