0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 9.0.0470: 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:   When in a loop and a closure refers to a variable declared in the
            loop, prepare for making a copy of variables for each closure.
This commit is contained in:
Bram Moolenaar
2022-09-15 17:19:37 +01:00
parent 3735f11050
commit b46c083a5e
8 changed files with 315 additions and 58 deletions

View File

@@ -504,7 +504,8 @@ call_dfunc(
// - if needed: a counter for number of closures created in
// ectx->ec_funcrefs.
varcount = dfunc->df_varcount + dfunc->df_has_closure;
if (GA_GROW_FAILS(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE + varcount))
if (GA_GROW_FAILS(&ectx->ec_stack,
arg_to_add + STACK_FRAME_SIZE + varcount))
return FAIL;
// If depth of calling is getting too high, don't execute the function.
@@ -553,6 +554,8 @@ call_dfunc(
{
typval_T *tv = STACK_TV_BOT(STACK_FRAME_SIZE + dfunc->df_varcount);
// Initialize the variable that counts how many closures were created.
// This is used in handle_closure_in_use().
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
}
@@ -1821,8 +1824,8 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
// The closure may need to find arguments and local variables in the
// current stack.
// The closure may need to find arguments and local variables of the
// current function in the stack.
pt->pt_outer.out_stack = &ectx->ec_stack;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
if (ectx->ec_outer_ref != NULL)
@@ -1836,8 +1839,9 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
}
}
// If this function returns and the closure is still being used, we
// need to make a copy of the context (arguments and local variables).
// 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.
// Store a reference to the partial so we can handle that.
if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1))
{
@@ -2477,6 +2481,7 @@ execute_unletrange(isn_T *iptr, ectx_T *ectx)
execute_for(isn_T *iptr, ectx_T *ectx)
{
typval_T *tv;
int jump = FALSE;
typval_T *ltv = STACK_TV_BOT(-1);
typval_T *idxtv =
STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
@@ -2492,9 +2497,7 @@ execute_for(isn_T *iptr, ectx_T *ectx)
if (list == NULL
|| idxtv->vval.v_number >= list->lv_len)
{
// past the end of the list, jump to "endfor"
ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&ectx->ec_funclocal);
jump = TRUE;
}
else if (list->lv_first == &range_list_item)
{
@@ -2524,9 +2527,7 @@ execute_for(isn_T *iptr, ectx_T *ectx)
++idxtv->vval.v_number;
if (str == NULL || str[idxtv->vval.v_number] == NUL)
{
// past the end of the string, jump to "endfor"
ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&ectx->ec_funclocal);
jump = TRUE;
}
else
{
@@ -2557,12 +2558,9 @@ execute_for(isn_T *iptr, ectx_T *ectx)
// The index is for the previous byte.
++idxtv->vval.v_number;
if (blob == NULL
|| idxtv->vval.v_number >= blob_len(blob))
if (blob == NULL || idxtv->vval.v_number >= blob_len(blob))
{
// past the end of the blob, jump to "endfor"
ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&ectx->ec_funclocal);
jump = TRUE;
}
else
{
@@ -2580,6 +2578,33 @@ execute_for(isn_T *iptr, ectx_T *ectx)
vartype_name(ltv->v_type));
return FAIL;
}
if (jump)
{
// past the end of the list/string/blob, jump to "endfor"
ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&ectx->ec_funclocal);
}
else
{
// Store the current number of funcrefs, this may be used in
// ISN_LOOPEND. The variable index is always one more than the loop
// variable index.
tv = STACK_TV_VAR(iptr->isn_arg.forloop.for_idx + 1);
tv->vval.v_number = ectx->ec_funcrefs.ga_len;
}
return OK;
}
/*
* End of a for or while loop: Handle any variables used by a closure.
*/
static int
execute_endloop(isn_T *iptr UNUSED, ectx_T *ectx UNUSED)
{
// TODO
return OK;
}
@@ -3989,6 +4014,31 @@ exec_instructions(ectx_T *ectx)
}
break;
// "while": jump to end if a condition is false
case ISN_WHILE:
{
int error = FALSE;
int jump = TRUE;
tv = STACK_TV_BOT(-1);
SOURCING_LNUM = iptr->isn_lnum;
jump = !tv_get_bool_chk(tv, &error);
if (error)
goto on_error;
// drop the value from the stack
clear_tv(tv);
--ectx->ec_stack.ga_len;
if (jump)
ectx->ec_iidx = iptr->isn_arg.whileloop.while_end;
// Store the current funccal count, may be used by
// ISN_LOOPEND later
tv = STACK_TV_VAR(
iptr->isn_arg.whileloop.while_funcref_idx);
tv->vval.v_number = ectx->ec_funcrefs.ga_len;
}
break;
// Jump if an argument with a default value was already set and not
// v:none.
case ISN_JUMP_IF_ARG_SET:
@@ -4005,6 +4055,12 @@ exec_instructions(ectx_T *ectx)
goto theend;
break;
// end of a for or while loop
case ISN_ENDLOOP:
if (execute_endloop(iptr, ectx) == FAIL)
goto theend;
break;
// start of ":try" block
case ISN_TRY:
{
@@ -6185,6 +6241,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case JUMP_IF_FALSE:
when = "JUMP_IF_FALSE";
break;
case JUMP_WHILE_FALSE:
when = "JUMP_WHILE_FALSE"; // unused
break;
case JUMP_IF_COND_FALSE:
when = "JUMP_IF_COND_FALSE";
break;
@@ -6212,6 +6271,27 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
}
break;
case ISN_ENDLOOP:
{
endloop_T *endloop = &iptr->isn_arg.endloop;
smsg("%s%4d ENDLOOP $%d save $%d - $%d", pfx, current,
endloop->end_funcref_idx,
endloop->end_var_idx,
endloop->end_var_idx + endloop->end_var_count - 1);
}
break;
case ISN_WHILE:
{
whileloop_T *whileloop = &iptr->isn_arg.whileloop;
smsg("%s%4d WHILE $%d -> %d", pfx, current,
whileloop->while_funcref_idx,
whileloop->while_end);
}
break;
case ISN_TRY:
{
try_T *try = &iptr->isn_arg.tryref;