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:
@@ -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;
|
||||
|
Reference in New Issue
Block a user