0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 9.0.0370: cleaning up afterwards can make a function messy

Problem:    Cleaning up afterwards can make a function messy.
Solution:   Add the :defer command.
This commit is contained in:
Bram Moolenaar
2022-09-03 21:35:53 +01:00
parent 06d32a0c17
commit 1d84f7608f
19 changed files with 632 additions and 158 deletions

View File

@@ -101,9 +101,15 @@ struct ectx_S {
static garray_T profile_info_ga = {0, 0, sizeof(profinfo_T), 20, NULL};
#endif
// Get pointer to item in the stack.
#define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
// Get pointer to item relative to the bottom of the stack, -1 is the last one.
#define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + (idx))
// Get pointer to a local variable on the stack. Negative for arguments.
#define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
void
to_string_error(vartype_T vartype)
{
@@ -610,9 +616,6 @@ call_dfunc(
return OK;
}
// Get pointer to item in the stack.
#define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
// Double linked list of funcstack_T in use.
static funcstack_T *first_funcstack = NULL;
@@ -842,6 +845,89 @@ set_ref_in_funcstacks(int copyID)
return FALSE;
}
/*
* Handle ISN_DEFER. Stack has a function reference and "argcount" arguments.
* The local variable that lists deferred functions is "var_idx".
* Returns OK or FAIL.
*/
static int
add_defer_func(int var_idx, int argcount, ectx_T *ectx)
{
typval_T *defer_tv = STACK_TV_VAR(var_idx);
list_T *defer_l;
typval_T *func_tv;
list_T *l;
int i;
typval_T listval;
if (defer_tv->v_type != VAR_LIST)
{
// first one, allocate the list
if (rettv_list_alloc(defer_tv) == FAIL)
return FAIL;
}
defer_l = defer_tv->vval.v_list;
l = list_alloc_with_items(argcount + 1);
if (l == NULL)
return FAIL;
listval.v_type = VAR_LIST;
listval.vval.v_list = l;
listval.v_lock = 0;
if (list_insert_tv(defer_l, &listval,
defer_l == NULL ? NULL : defer_l->lv_first) == FAIL)
{
vim_free(l);
return FAIL;
}
func_tv = STACK_TV_BOT(-argcount - 1);
// TODO: check type is a funcref
list_set_item(l, 0, func_tv);
for (i = 1; i <= argcount; ++i)
list_set_item(l, i, STACK_TV_BOT(-argcount + i - 1));
ectx->ec_stack.ga_len -= argcount + 1;
return OK;
}
/*
* Invoked when returning from a function: Invoke any deferred calls.
*/
static void
invoke_defer_funcs(ectx_T *ectx)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
typval_T *defer_tv = STACK_TV_VAR(dfunc->df_defer_var_idx - 1);
listitem_T *li;
if (defer_tv->v_type != VAR_LIST)
return; // no function added
for (li = defer_tv->vval.v_list->lv_first; li != NULL; li = li->li_next)
{
list_T *l = li->li_tv.vval.v_list;
typval_T rettv;
typval_T argvars[MAX_FUNC_ARGS];
int i;
listitem_T *arg_li = l->lv_first;
funcexe_T funcexe;
for (i = 0; i < l->lv_len - 1; ++i)
{
arg_li = arg_li->li_next;
argvars[i] = arg_li->li_tv;
}
CLEAR_FIELD(funcexe);
funcexe.fe_evaluate = TRUE;
rettv.v_type = VAR_UNKNOWN;
(void)call_func(l->lv_first->li_tv.vval.v_string, -1,
&rettv, l->lv_len - 1, argvars, &funcexe);
clear_tv(&rettv);
}
}
/*
* Return from the current function.
*/
@@ -876,6 +962,9 @@ func_return(ectx_T *ectx)
}
#endif
if (dfunc->df_defer_var_idx > 0)
invoke_defer_funcs(ectx);
// No check for uf_refcount being zero, cannot think of a way that would
// happen.
--dfunc->df_ufunc->uf_calls;
@@ -949,8 +1038,6 @@ func_return(ectx_T *ectx)
return OK;
}
#undef STACK_TV
/*
* Prepare arguments and rettv for calling a builtin or user function.
*/
@@ -1732,16 +1819,6 @@ typedef struct subs_expr_S {
int subs_status;
} subs_expr_T;
// Get pointer to item in the stack.
#define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
// Get pointer to item at the bottom of the stack, -1 is the bottom.
#undef STACK_TV_BOT
#define STACK_TV_BOT(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_stack.ga_len + idx)
// Get pointer to a local variable on the stack. Negative for arguments.
#define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
// Set when calling do_debug().
static ectx_T *debug_context = NULL;
static int debug_var_count;
@@ -3670,6 +3747,13 @@ exec_instructions(ectx_T *ectx)
}
break;
// :defer func(arg)
case ISN_DEFER:
if (add_defer_func(iptr->isn_arg.defer.defer_var_idx,
iptr->isn_arg.defer.defer_argcount, ectx) == FAIL)
goto on_error;
break;
// return from a :def function call without a value
case ISN_RETURN_VOID:
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
@@ -5024,6 +5108,14 @@ on_fatal_error:
done:
ret = OK;
theend:
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
if (dfunc->df_defer_var_idx > 0)
invoke_defer_funcs(ectx);
}
dict_stack_clear(dict_stack_len_at_start);
ectx->ec_trylevel_at_start = save_trylevel_at_start;
return ret;
@@ -5903,6 +5995,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case ISN_PCALL_END:
smsg("%s%4d PCALL end", pfx, current);
break;
case ISN_DEFER:
smsg("%s%4d DEFER %d args", pfx, current,
(int)iptr->isn_arg.defer.defer_argcount);
break;
case ISN_RETURN:
smsg("%s%4d RETURN", pfx, current);
break;