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

@ -2979,6 +2979,62 @@ is used as a method: >
let y = GetList()->Filter() let y = GetList()->Filter()
CLEANING UP IN A FUNCTION ~
*:defer*
:defer {func}({args}) Call {func} when the current function is done.
{args} are evaluated here.
Quite often a command in a function has a global effect, which must be undone
when the function finishes. Handling this in all kinds of situations can be a
hassle. Especially when an unexpected error is encountered. This can be done
with `try` / `finally` blocks, but this gets complicated when there is more
than one.
A much simpler solution is using `defer`. It schedules a function call when
the function is returning, no matter if there is an error. Example: >
func Filter(text)
call writefile(a:text, 'Tempfile')
call system('filter < Tempfile > Outfile')
call Handle('Outfile')
call delete('Tempfile')
call delete('Outfile')
endfunc
Here 'Tempfile' and 'Outfile' will not be deleted if something causes the
function to abort. `:defer` can be used to avoid that: >
func Filter(text)
call writefile(a:text, 'Tempfile')
defer delete('Tempfile')
defer delete('Outfile')
call system('filter < Tempfile > Outfile')
call Handle('Outfile')
endfunc
Note that deleting "Outfile" is scheduled before calling system(), since it
can be created even when `system()` fails.
The defered functions are called in reverse order, the last one added is
executed first. A useless example: >
func Useless()
for s in range(3)
defer execute('echomsg "number ' .. s .. '"')
endfor
endfunc
Now `:messages` shows:
number 2
number 1
number 0
Any return value of the deferred function is discarded. The function cannot
be followed by anything, such as "->func" or ".member". Currently `:defer
GetArg()->TheFunc()` does not work, it may work in a later version.
Errors are reported but do not cause aborting execution of deferred functions.
No range is accepted.
AUTOMATICALLY LOADING FUNCTIONS ~ AUTOMATICALLY LOADING FUNCTIONS ~
*autoload-functions* *autoload-functions*
When using many or large functions, it's possible to automatically define them When using many or large functions, it's possible to automatically define them

View File

@ -9,28 +9,28 @@ static const unsigned short cmdidxs1[26] =
/* b */ 21, /* b */ 21,
/* c */ 45, /* c */ 45,
/* d */ 112, /* d */ 112,
/* e */ 137, /* e */ 138,
/* f */ 166, /* f */ 167,
/* g */ 183, /* g */ 184,
/* h */ 189, /* h */ 190,
/* i */ 199, /* i */ 200,
/* j */ 219, /* j */ 220,
/* k */ 221, /* k */ 222,
/* l */ 226, /* l */ 227,
/* m */ 289, /* m */ 290,
/* n */ 307, /* n */ 308,
/* o */ 327, /* o */ 328,
/* p */ 339, /* p */ 340,
/* q */ 378, /* q */ 379,
/* r */ 381, /* r */ 382,
/* s */ 401, /* s */ 402,
/* t */ 471, /* t */ 472,
/* u */ 517, /* u */ 518,
/* v */ 528, /* v */ 529,
/* w */ 549, /* w */ 550,
/* x */ 563, /* x */ 564,
/* y */ 573, /* y */ 574,
/* z */ 574 /* z */ 575
}; };
/* /*
@ -44,7 +44,7 @@ static const unsigned char cmdidxs2[26][26] =
/* a */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 7, 0, 0, 0, 8, 17, 0, 18, 0, 0, 0, 0, 0 }, /* a */ { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 7, 0, 0, 0, 8, 17, 0, 18, 0, 0, 0, 0, 0 },
/* b */ { 2, 0, 0, 5, 6, 8, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 23, 0, 0, 0 }, /* b */ { 2, 0, 0, 5, 6, 8, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 23, 0, 0, 0 },
/* c */ { 3, 12, 16, 18, 20, 22, 25, 0, 0, 0, 0, 33, 38, 41, 47, 57, 59, 60, 61, 0, 63, 0, 66, 0, 0, 0 }, /* c */ { 3, 12, 16, 18, 20, 22, 25, 0, 0, 0, 0, 33, 38, 41, 47, 57, 59, 60, 61, 0, 63, 0, 66, 0, 0, 0 },
/* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 18, 0, 19, 0, 0, 20, 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, 0 }, /* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 9, 19, 0, 20, 0, 0, 21, 0, 0, 23, 24, 0, 0, 0, 0, 0, 0, 0 },
/* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 9, 11, 12, 0, 0, 0, 0, 0, 0, 0, 23, 0, 24, 0, 0 }, /* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 9, 11, 12, 0, 0, 0, 0, 0, 0, 0, 23, 0, 24, 0, 0 },
/* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0 }, /* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0 },
/* g */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0 }, /* g */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0 },
@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
/* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } /* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
}; };
static const int command_count = 591; static const int command_count = 592;

View File

@ -467,6 +467,9 @@ EXCMD(CMD_def, "def", ex_function,
EXCMD(CMD_defcompile, "defcompile", ex_defcompile, EXCMD(CMD_defcompile, "defcompile", ex_defcompile,
EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_TRLBAR|EX_EXTRA, EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_TRLBAR|EX_EXTRA,
ADDR_NONE), ADDR_NONE),
EXCMD(CMD_defer, "defer", ex_call,
EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_delcommand, "delcommand", ex_delcommand, EXCMD(CMD_delcommand, "delcommand", ex_delcommand,
EX_NEEDARG|EX_WORD1|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, EX_NEEDARG|EX_WORD1|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE), ADDR_NONE),

View File

@ -58,6 +58,7 @@ void func_ptr_unref(ufunc_T *fp);
void func_ref(char_u *name); void func_ref(char_u *name);
void func_ptr_ref(ufunc_T *fp); void func_ptr_ref(ufunc_T *fp);
void ex_return(exarg_T *eap); void ex_return(exarg_T *eap);
void handle_defer(void);
void ex_call(exarg_T *eap); void ex_call(exarg_T *eap);
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv); int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
void discard_pending_return(void *rettv); void discard_pending_return(void *rettv);

View File

@ -21,6 +21,7 @@ char_u *compile_finally(char_u *arg, cctx_T *cctx);
char_u *compile_endtry(char_u *arg, cctx_T *cctx); char_u *compile_endtry(char_u *arg, cctx_T *cctx);
char_u *compile_throw(char_u *arg, cctx_T *cctx); char_u *compile_throw(char_u *arg, cctx_T *cctx);
char_u *compile_eval(char_u *arg, cctx_T *cctx); char_u *compile_eval(char_u *arg, cctx_T *cctx);
char_u *compile_defer(char_u *arg_start, cctx_T *cctx);
char_u *compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx); char_u *compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx);
char_u *compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx); char_u *compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx);
char_u *compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx); char_u *compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx);

View File

@ -4,6 +4,7 @@ void clear_ppconst(ppconst_T *ppconst);
int compile_member(int is_slice, int *keeping_dict, cctx_T *cctx); int compile_member(int is_slice, int *keeping_dict, cctx_T *cctx);
int compile_load_scriptvar(cctx_T *cctx, char_u *name, char_u *start, char_u **end); int compile_load_scriptvar(cctx_T *cctx, char_u *name, char_u *start, char_u **end);
int compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int is_expr, int error); int compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int is_expr, int error);
int compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, ca_special_T special_fn);
char_u *to_name_end(char_u *arg, int use_namespace); char_u *to_name_end(char_u *arg, int use_namespace);
char_u *to_name_const_end(char_u *arg); char_u *to_name_const_end(char_u *arg);
int get_lambda_tv_and_compile(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg); int get_lambda_tv_and_compile(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);

View File

@ -23,7 +23,7 @@ int generate_PUSHS(cctx_T *cctx, char_u **str);
int generate_PUSHCHANNEL(cctx_T *cctx); int generate_PUSHCHANNEL(cctx_T *cctx);
int generate_PUSHJOB(cctx_T *cctx); int generate_PUSHJOB(cctx_T *cctx);
int generate_PUSHBLOB(cctx_T *cctx, blob_T *blob); int generate_PUSHBLOB(cctx_T *cctx, blob_T *blob);
int generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type); int generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type, int may_prefix);
int generate_AUTOLOAD(cctx_T *cctx, char_u *name, type_T *type); int generate_AUTOLOAD(cctx_T *cctx, char_u *name, type_T *type);
int generate_GETITEM(cctx_T *cctx, int index, int with_op); int generate_GETITEM(cctx_T *cctx, int index, int with_op);
int generate_SLICE(cctx_T *cctx, int count); int generate_SLICE(cctx_T *cctx, int count);
@ -52,6 +52,7 @@ int generate_BLOBAPPEND(cctx_T *cctx);
int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount); int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
int generate_UCALL(cctx_T *cctx, char_u *name, int argcount); int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int at_top); int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int at_top);
int generate_DEFER(cctx_T *cctx, int var_idx, int argcount);
int generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len); int generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len);
int generate_ECHO(cctx_T *cctx, int with_white, int count); int generate_ECHO(cctx_T *cctx, int with_white, int count);
int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count); int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count);

View File

@ -1753,6 +1753,7 @@ struct funccall_S
linenr_T breakpoint; // next line with breakpoint or zero linenr_T breakpoint; // next line with breakpoint or zero
int dbg_tick; // debug_tick when breakpoint was set int dbg_tick; // debug_tick when breakpoint was set
int level; // top nesting level of executed function int level; // top nesting level of executed function
garray_T fc_defer; // functions to be called on return
#ifdef FEAT_PROFILE #ifdef FEAT_PROFILE
proftime_T prof_child; // time spent in a child proftime_T prof_child; // time spent in a child
#endif #endif
@ -1767,6 +1768,14 @@ struct funccall_S
// "func" // "func"
}; };
// structure used as item in "fc_defer"
typedef struct
{
char_u *dr_name; // function name, allocated
typval_T dr_argvars[MAX_FUNC_ARGS + 1];
int dr_argcount;
} defer_T;
/* /*
* Struct used by trans_function_name() * Struct used by trans_function_name()
*/ */
@ -2850,7 +2859,7 @@ struct file_buffer
int b_u_synced; // entry lists are synced int b_u_synced; // entry lists are synced
long b_u_seq_last; // last used undo sequence number long b_u_seq_last; // last used undo sequence number
long b_u_save_nr_last; // counter for last file write long b_u_save_nr_last; // counter for last file write
long b_u_seq_cur; // hu_seq of header below which we are now long b_u_seq_cur; // uh_seq of header below which we are now
time_T b_u_time_cur; // uh_time of header below which we are now time_T b_u_time_cur; // uh_time of header below which we are now
long b_u_save_nr_cur; // file write nr after which we are now long b_u_save_nr_cur; // file write nr after which we are now

View File

@ -529,4 +529,36 @@ func Test_funcdef_alloc_failure()
bw! bw!
endfunc endfunc
func AddDefer(arg)
call extend(g:deferred, [a:arg])
endfunc
func WithDeferTwo()
call extend(g:deferred, ['in Two'])
for nr in range(3)
defer AddDefer('Two' .. nr)
endfor
call extend(g:deferred, ['end Two'])
endfunc
func WithDeferOne()
call extend(g:deferred, ['in One'])
call writefile(['text'], 'Xfuncdefer')
defer delete('Xfuncdefer')
defer AddDefer('One')
call WithDeferTwo()
call extend(g:deferred, ['end One'])
endfunc
func Test_defer()
let g:deferred = []
call WithDeferOne()
call assert_equal(['in One', 'in Two', 'end Two', 'Two2', 'Two1', 'Two0', 'end One', 'One'], g:deferred)
unlet g:deferred
call assert_equal('', glob('Xfuncdefer'))
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@ -2900,4 +2900,19 @@ def Test_disassemble_bitshift()
'10 RETURN void', instr) '10 RETURN void', instr)
enddef enddef
def s:OneDefer()
defer delete("file")
enddef
def Test_disassemble_defer()
var instr = execute('disassemble s:OneDefer')
assert_match('OneDefer\_s*' ..
'defer delete("file")\_s*' ..
'\d PUSHFUNC "delete"\_s*' ..
'\d PUSHS "file"\_s*' ..
'\d DEFER 1 args\_s*' ..
'\d RETURN\_s*',
instr)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@ -4272,6 +4272,36 @@ def Test_cexpr_errmsg_line_number()
v9.CheckScriptFailure(lines, 'E777', 2) v9.CheckScriptFailure(lines, 'E777', 2)
enddef enddef
def AddDefer(s: string)
g:deferred->extend([s])
enddef
def DeferTwo()
g:deferred->extend(['in Two'])
for n in range(3)
defer g:AddDefer('two' .. n)
endfor
g:deferred->extend(['end Two'])
enddef
def DeferOne()
g:deferred->extend(['in One'])
defer g:AddDefer('one')
g:DeferTwo()
g:deferred->extend(['end One'])
writefile(['text'], 'XdeferFile')
defer delete('XdeferFile')
enddef
def Test_defer()
g:deferred = []
g:DeferOne()
assert_equal(['in One', 'in Two', 'end Two', 'two2', 'two1', 'two0', 'end One', 'one'], g:deferred)
unlet g:deferred
assert_equal('', glob('XdeferFile'))
enddef
" The following messes up syntax highlight, keep near the end. " The following messes up syntax highlight, keep near the end.
if has('python3') if has('python3')
def Test_python3_command() def Test_python3_command()

View File

@ -1728,44 +1728,36 @@ emsg_funcname(char *ermsg, char_u *name)
} }
/* /*
* Allocate a variable for the result of a function. * Get function arguments at "*arg" and advance it.
* Return OK or FAIL. * Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
*/ */
int static int
get_func_tv( get_func_arguments(
char_u *name, // name of the function char_u **arg,
int len, // length of "name" or -1 to use strlen() evalarg_T *evalarg,
typval_T *rettv, int partial_argc,
char_u **arg, // argument, pointing to the '(' typval_T *argvars,
evalarg_T *evalarg, // for line continuation int *argcount)
funcexe_T *funcexe) // various values
{ {
char_u *argp; char_u *argp = *arg;
int ret = OK; int ret = OK;
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
int argcount = 0; // number of arguments found
int vim9script = in_vim9script(); int vim9script = in_vim9script();
int evaluate = evalarg == NULL int evaluate = evalarg == NULL
? FALSE : (evalarg->eval_flags & EVAL_EVALUATE); ? FALSE : (evalarg->eval_flags & EVAL_EVALUATE);
/* while (*argcount < MAX_FUNC_ARGS - partial_argc)
* Get the arguments.
*/
argp = *arg;
while (argcount < MAX_FUNC_ARGS - (funcexe->fe_partial == NULL ? 0
: funcexe->fe_partial->pt_argc))
{ {
// skip the '(' or ',' and possibly line breaks // skip the '(' or ',' and possibly line breaks
argp = skipwhite_and_linebreak(argp + 1, evalarg); argp = skipwhite_and_linebreak(argp + 1, evalarg);
if (*argp == ')' || *argp == ',' || *argp == NUL) if (*argp == ')' || *argp == ',' || *argp == NUL)
break; break;
if (eval1(&argp, &argvars[argcount], evalarg) == FAIL) if (eval1(&argp, &argvars[*argcount], evalarg) == FAIL)
{ {
ret = FAIL; ret = FAIL;
break; break;
} }
++argcount; ++*argcount;
// The comma should come right after the argument, but this wasn't // The comma should come right after the argument, but this wasn't
// checked previously, thus only enforce it in Vim9 script. // checked previously, thus only enforce it in Vim9 script.
if (vim9script) if (vim9script)
@ -1791,11 +1783,41 @@ get_func_tv(
break; break;
} }
} }
argp = skipwhite_and_linebreak(argp, evalarg); argp = skipwhite_and_linebreak(argp, evalarg);
if (*argp == ')') if (*argp == ')')
++argp; ++argp;
else else
ret = FAIL; ret = FAIL;
*arg = argp;
return ret;
}
/*
* Call a function and put the result in "rettv".
* Return OK or FAIL.
*/
int
get_func_tv(
char_u *name, // name of the function
int len, // length of "name" or -1 to use strlen()
typval_T *rettv,
char_u **arg, // argument, pointing to the '('
evalarg_T *evalarg, // for line continuation
funcexe_T *funcexe) // various values
{
char_u *argp;
int ret = OK;
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
int argcount = 0; // number of arguments found
int vim9script = in_vim9script();
int evaluate = evalarg == NULL
? FALSE : (evalarg->eval_flags & EVAL_EVALUATE);
argp = *arg;
ret = get_func_arguments(&argp, evalarg,
(funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc),
argvars, &argcount);
if (ret == OK) if (ret == OK)
{ {
@ -2884,6 +2906,9 @@ call_user_func(
do_cmdline(NULL, get_func_line, (void *)fc, do_cmdline(NULL, get_func_line, (void *)fc,
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
// Invoke functions added with ":defer".
handle_defer();
--RedrawingDisabled; --RedrawingDisabled;
// when the function was aborted because of an error, return -1 // when the function was aborted because of an error, return -1
@ -5457,8 +5482,164 @@ ex_return(exarg_T *eap)
clear_evalarg(&evalarg, eap); clear_evalarg(&evalarg, eap);
} }
static int
ex_call_inner(
exarg_T *eap,
char_u *name,
char_u **arg,
char_u *startarg,
funcexe_T *funcexe_init,
evalarg_T *evalarg)
{
linenr_T lnum;
int doesrange;
typval_T rettv;
int failed = FALSE;
/*
* When skipping, evaluate the function once, to find the end of the
* arguments.
* When the function takes a range, this is discovered after the first
* call, and the loop is broken.
*/
if (eap->skip)
{
++emsg_skip;
lnum = eap->line2; // do it once, also with an invalid range
}
else
lnum = eap->line1;
for ( ; lnum <= eap->line2; ++lnum)
{
funcexe_T funcexe;
if (!eap->skip && eap->addr_count > 0)
{
if (lnum > curbuf->b_ml.ml_line_count)
{
// If the function deleted lines or switched to another buffer
// the line number may become invalid.
emsg(_(e_invalid_range));
break;
}
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
}
*arg = startarg;
funcexe = *funcexe_init;
funcexe.fe_doesrange = &doesrange;
rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
if (get_func_tv(name, -1, &rettv, arg, evalarg, &funcexe) == FAIL)
{
failed = TRUE;
break;
}
if (has_watchexpr())
dbg_check_breakpoint(eap);
// Handle a function returning a Funcref, Dictionary or List.
if (handle_subscript(arg, NULL, &rettv,
eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL)
{
failed = TRUE;
break;
}
clear_tv(&rettv);
if (doesrange || eap->skip)
break;
// Stop when immediately aborting on error, or when an interrupt
// occurred or an exception was thrown but not caught.
// get_func_tv() returned OK, so that the check for trailing
// characters below is executed.
if (aborting())
break;
}
if (eap->skip)
--emsg_skip;
return failed;
}
/*
* Core part of ":defer func(arg)". "arg" points to the "(" and is advanced.
* Returns FAIL or OK.
*/
static int
ex_defer_inner(char_u *name, char_u **arg, evalarg_T *evalarg)
{
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
int argcount = 0; // number of arguments found
defer_T *dr;
int ret = FAIL;
char_u *saved_name;
if (current_funccal == NULL)
{
semsg(_(e_str_not_inside_function), "defer");
return FAIL;
}
if (get_func_arguments(arg, evalarg, FALSE, argvars, &argcount) == FAIL)
goto theend;
saved_name = vim_strsave(name);
if (saved_name == NULL)
goto theend;
if (current_funccal->fc_defer.ga_itemsize == 0)
ga_init2(&current_funccal->fc_defer, sizeof(defer_T), 10);
if (ga_grow(&current_funccal->fc_defer, 1) == FAIL)
goto theend;
dr = ((defer_T *)current_funccal->fc_defer.ga_data)
+ current_funccal->fc_defer.ga_len++;
dr->dr_name = saved_name;
dr->dr_argcount = argcount;
while (argcount > 0)
{
--argcount;
dr->dr_argvars[argcount] = argvars[argcount];
}
ret = OK;
theend:
while (--argcount >= 0)
clear_tv(&argvars[argcount]);
return ret;
}
/*
* Invoked after a functions has finished: invoke ":defer" functions.
*/
void
handle_defer(void)
{
int idx;
for (idx = current_funccal->fc_defer.ga_len - 1; idx >= 0; --idx)
{
funcexe_T funcexe;
typval_T rettv;
defer_T *dr = ((defer_T *)current_funccal->fc_defer.ga_data) + idx;
int i;
CLEAR_FIELD(funcexe);
funcexe.fe_evaluate = TRUE;
rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this
call_func(dr->dr_name, -1, &rettv,
dr->dr_argcount, dr->dr_argvars, &funcexe);
clear_tv(&rettv);
vim_free(dr->dr_name);
for (i = dr->dr_argcount - 1; i >= 0; --i)
clear_tv(&dr->dr_argvars[i]);
}
ga_clear(&current_funccal->fc_defer);
}
/* /*
* ":1,25call func(arg1, arg2)" function call. * ":1,25call func(arg1, arg2)" function call.
* ":defer func(arg1, arg2)" deferred function call.
*/ */
void void
ex_call(exarg_T *eap) ex_call(exarg_T *eap)
@ -5468,9 +5649,6 @@ ex_call(exarg_T *eap)
char_u *name; char_u *name;
char_u *tofree; char_u *tofree;
int len; int len;
typval_T rettv;
linenr_T lnum;
int doesrange;
int failed = FALSE; int failed = FALSE;
funcdict_T fudi; funcdict_T fudi;
partial_T *partial = NULL; partial_T *partial = NULL;
@ -5482,6 +5660,8 @@ ex_call(exarg_T *eap)
fill_evalarg_from_eap(&evalarg, eap, eap->skip); fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) if (eap->skip)
{ {
typval_T rettv;
// trans_function_name() doesn't work well when skipping, use eval0() // trans_function_name() doesn't work well when skipping, use eval0()
// instead to skip to any following command, e.g. for: // instead to skip to any following command, e.g. for:
// :if 0 | call dict.foo().bar() | endif // :if 0 | call dict.foo().bar() | endif
@ -5531,82 +5711,29 @@ ex_call(exarg_T *eap)
goto end; goto end;
} }
/* if (eap->cmdidx == CMD_defer)
* When skipping, evaluate the function once, to find the end of the
* arguments.
* When the function takes a range, this is discovered after the first
* call, and the loop is broken.
*/
if (eap->skip)
{ {
++emsg_skip; arg = startarg;
lnum = eap->line2; // do it once, also with an invalid range failed = ex_defer_inner(name, &arg, &evalarg) == FAIL;
} }
else else
lnum = eap->line1;
for ( ; lnum <= eap->line2; ++lnum)
{ {
funcexe_T funcexe; funcexe_T funcexe;
if (!eap->skip && eap->addr_count > 0)
{
if (lnum > curbuf->b_ml.ml_line_count)
{
// If the function deleted lines or switched to another buffer
// the line number may become invalid.
emsg(_(e_invalid_range));
break;
}
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = 0;
curwin->w_cursor.coladd = 0;
}
arg = startarg;
CLEAR_FIELD(funcexe); CLEAR_FIELD(funcexe);
funcexe.fe_firstline = eap->line1; funcexe.fe_check_type = type;
funcexe.fe_lastline = eap->line2;
funcexe.fe_doesrange = &doesrange;
funcexe.fe_evaluate = !eap->skip;
funcexe.fe_partial = partial; funcexe.fe_partial = partial;
funcexe.fe_selfdict = fudi.fd_dict; funcexe.fe_selfdict = fudi.fd_dict;
funcexe.fe_check_type = type; funcexe.fe_firstline = eap->line1;
funcexe.fe_lastline = eap->line2;
funcexe.fe_found_var = found_var; funcexe.fe_found_var = found_var;
rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this funcexe.fe_evaluate = !eap->skip;
if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL) failed = ex_call_inner(eap, name, &arg, startarg, &funcexe, &evalarg);
{
failed = TRUE;
break;
} }
if (has_watchexpr())
dbg_check_breakpoint(eap);
// Handle a function returning a Funcref, Dictionary or List.
if (handle_subscript(&arg, NULL, &rettv,
eap->skip ? NULL : &EVALARG_EVALUATE, TRUE) == FAIL)
{
failed = TRUE;
break;
}
clear_tv(&rettv);
if (doesrange || eap->skip)
break;
// Stop when immediately aborting on error, or when an interrupt
// occurred or an exception was thrown but not caught.
// get_func_tv() returned OK, so that the check for trailing
// characters below is executed.
if (aborting())
break;
}
if (eap->skip)
--emsg_skip;
// When inside :try we need to check for following "| catch" or "| endtry". // When inside :try we need to check for following "| catch" or "| endtry".
// Not when there was an error, but do check if an exception was thrown. // Not when there was an error, but do check if an exception was thrown.
if ((!aborting() || did_throw) if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 0))
&& (!failed || eap->cstack->cs_trylevel > 0))
{ {
// Check for trailing illegal characters and a following command. // Check for trailing illegal characters and a following command.
arg = skipwhite(arg); arg = skipwhite(arg);

View File

@ -707,6 +707,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 */
/**/
370,
/**/ /**/
369, 369,
/**/ /**/

View File

@ -113,6 +113,7 @@ typedef enum {
ISN_FUNCREF, // push a function ref to dfunc isn_arg.funcref ISN_FUNCREF, // push a function ref to dfunc isn_arg.funcref
ISN_NEWFUNC, // create a global function from a lambda function ISN_NEWFUNC, // create a global function from a lambda function
ISN_DEF, // list functions ISN_DEF, // list functions
ISN_DEFER, // :defer argument count is isn_arg.number
// expression operations // expression operations
ISN_JUMP, // jump if condition is matched isn_arg.jump ISN_JUMP, // jump if condition is matched isn_arg.jump
@ -419,6 +420,12 @@ typedef struct {
int dbg_break_lnum; // first line to break after int dbg_break_lnum; // first line to break after
} debug_T; } debug_T;
// arguments to ISN_DEFER
typedef struct {
int defer_var_idx; // local variable index for defer list
int defer_argcount; // number of arguments
} deferins_T;
/* /*
* Instruction * Instruction
*/ */
@ -468,6 +475,7 @@ struct isn_S {
tobool_T tobool; tobool_T tobool;
getitem_T getitem; getitem_T getitem;
debug_T debug; debug_T debug;
deferins_T defer;
} isn_arg; } isn_arg;
}; };
@ -498,6 +506,9 @@ struct dfunc_S {
int df_varcount; // number of local variables int df_varcount; // number of local variables
int df_has_closure; // one if a closure was created int df_has_closure; // one if a closure was created
int df_defer_var_idx; // index of local variable that has a list
// of deferred function calls; zero if not
// set
}; };
// Number of entries used by stack frame for a function call. // Number of entries used by stack frame for a function call.
@ -735,6 +746,15 @@ struct cctx_S {
// lhs_name is not NULL // lhs_name is not NULL
}; };
/*
* List of special functions for "compile_arguments()".
*/
typedef enum {
CA_NOT_SPECIAL,
CA_SEARCHPAIR, // {skip} in searchpair() and searchpairpos()
CA_SUBSTITUTE, // {sub} in substitute(), when prefixed with \=
} ca_special_T;
// flags for typval2type() // flags for typval2type()
#define TVTT_DO_MEMBER 1 #define TVTT_DO_MEMBER 1
#define TVTT_MORE_SPECIFIC 2 // get most specific type for member #define TVTT_MORE_SPECIFIC 2 // get most specific type for member

View File

@ -1654,6 +1654,9 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
return p; return p;
} }
/*
* Compile an expression or function call.
*/
char_u * char_u *
compile_eval(char_u *arg, cctx_T *cctx) compile_eval(char_u *arg, cctx_T *cctx)
{ {
@ -1681,6 +1684,67 @@ compile_eval(char_u *arg, cctx_T *cctx)
return skipwhite(p); return skipwhite(p);
} }
/*
* Compile "defer func(arg)".
*/
char_u *
compile_defer(char_u *arg_start, cctx_T *cctx)
{
char_u *p;
char_u *arg = arg_start;
int argcount = 0;
dfunc_T *dfunc;
type_T *type;
int func_idx;
// Get a funcref for the function name.
// TODO: better way to find the "(".
p = vim_strchr(arg, '(');
if (p == NULL)
{
semsg(_(e_missing_parenthesis_str), arg);
return NULL;
}
*p = NUL;
func_idx = find_internal_func(arg);
if (func_idx >= 0)
// TODO: better type
generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx),
&t_func_any, FALSE);
else if (compile_expr0(&arg, cctx) == FAIL)
return NULL;
*p = '(';
// check for function type
type = get_type_on_stack(cctx, 0);
if (type->tt_type != VAR_FUNC)
{
emsg(_(e_function_name_required));
return NULL;
}
// compile the arguments
arg = skipwhite(p + 1);
if (compile_arguments(&arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
return NULL;
// TODO: check argument count with "type"
dfunc = ((dfunc_T *)def_functions.ga_data) + cctx->ctx_ufunc->uf_dfunc_idx;
if (dfunc->df_defer_var_idx == 0)
{
lvar_T *lvar = reserve_local(cctx, (char_u *)"@defer@", 7,
TRUE, &t_list_any);
if (lvar == NULL)
return NULL;
dfunc->df_defer_var_idx = lvar->lv_idx + 1;
}
if (generate_DEFER(cctx, dfunc->df_defer_var_idx - 1, argcount) == FAIL)
return NULL;
return skipwhite(arg);
}
/* /*
* compile "echo expr" * compile "echo expr"
* compile "echomsg expr" * compile "echomsg expr"

View File

@ -2373,7 +2373,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
r = generate_PUSHBLOB(cctx, blob_alloc()); r = generate_PUSHBLOB(cctx, blob_alloc());
break; break;
case VAR_FUNC: case VAR_FUNC:
r = generate_PUSHFUNC(cctx, NULL, &t_func_void); r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE);
break; break;
case VAR_LIST: case VAR_LIST:
r = generate_NEWLIST(cctx, 0, FALSE); r = generate_NEWLIST(cctx, 0, FALSE);
@ -2748,6 +2748,7 @@ compile_def_function(
// Was compiled in this mode before: Free old instructions. // Was compiled in this mode before: Free old instructions.
delete_def_function_contents(dfunc, FALSE); delete_def_function_contents(dfunc, FALSE);
ga_clear_strings(&dfunc->df_var_names); ga_clear_strings(&dfunc->df_var_names);
dfunc->df_defer_var_idx = 0;
} }
else else
{ {
@ -3249,6 +3250,10 @@ compile_def_function(
line = compile_eval(p, &cctx); line = compile_eval(p, &cctx);
break; break;
case CMD_defer:
line = compile_defer(p, &cctx);
break;
case CMD_echo: case CMD_echo:
case CMD_echon: case CMD_echon:
case CMD_echoconsole: case CMD_echoconsole:

View File

@ -101,9 +101,15 @@ struct ectx_S {
static garray_T profile_info_ga = {0, 0, sizeof(profinfo_T), 20, NULL}; static garray_T profile_info_ga = {0, 0, sizeof(profinfo_T), 20, NULL};
#endif #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. // 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)) #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 void
to_string_error(vartype_T vartype) to_string_error(vartype_T vartype)
{ {
@ -610,9 +616,6 @@ call_dfunc(
return OK; 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. // Double linked list of funcstack_T in use.
static funcstack_T *first_funcstack = NULL; static funcstack_T *first_funcstack = NULL;
@ -842,6 +845,89 @@ set_ref_in_funcstacks(int copyID)
return FALSE; 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. * Return from the current function.
*/ */
@ -876,6 +962,9 @@ func_return(ectx_T *ectx)
} }
#endif #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 // No check for uf_refcount being zero, cannot think of a way that would
// happen. // happen.
--dfunc->df_ufunc->uf_calls; --dfunc->df_ufunc->uf_calls;
@ -949,8 +1038,6 @@ func_return(ectx_T *ectx)
return OK; return OK;
} }
#undef STACK_TV
/* /*
* Prepare arguments and rettv for calling a builtin or user function. * Prepare arguments and rettv for calling a builtin or user function.
*/ */
@ -1732,16 +1819,6 @@ typedef struct subs_expr_S {
int subs_status; int subs_status;
} subs_expr_T; } 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(). // Set when calling do_debug().
static ectx_T *debug_context = NULL; static ectx_T *debug_context = NULL;
static int debug_var_count; static int debug_var_count;
@ -3670,6 +3747,13 @@ exec_instructions(ectx_T *ectx)
} }
break; 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 // return from a :def function call without a value
case ISN_RETURN_VOID: case ISN_RETURN_VOID:
if (GA_GROW_FAILS(&ectx->ec_stack, 1)) if (GA_GROW_FAILS(&ectx->ec_stack, 1))
@ -5024,6 +5108,14 @@ on_fatal_error:
done: done:
ret = OK; ret = OK;
theend: 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); dict_stack_clear(dict_stack_len_at_start);
ectx->ec_trylevel_at_start = save_trylevel_at_start; ectx->ec_trylevel_at_start = save_trylevel_at_start;
return ret; return ret;
@ -5903,6 +5995,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case ISN_PCALL_END: case ISN_PCALL_END:
smsg("%s%4d PCALL end", pfx, current); smsg("%s%4d PCALL end", pfx, current);
break; break;
case ISN_DEFER:
smsg("%s%4d DEFER %d args", pfx, current,
(int)iptr->isn_arg.defer.defer_argcount);
break;
case ISN_RETURN: case ISN_RETURN:
smsg("%s%4d RETURN", pfx, current); smsg("%s%4d RETURN", pfx, current);
break; break;

View File

@ -313,7 +313,7 @@ compile_load_scriptvar(
// name. If a '(' follows it must be a function. Otherwise we // name. If a '(' follows it must be a function. Otherwise we
// don't know, it can be "script.Func". // don't know, it can be "script.Func".
if (cc == '(' || paren_follows_after_expr) if (cc == '(' || paren_follows_after_expr)
res = generate_PUSHFUNC(cctx, auto_name, &t_func_any); res = generate_PUSHFUNC(cctx, auto_name, &t_func_any, TRUE);
else else
res = generate_AUTOLOAD(cctx, auto_name, &t_any); res = generate_AUTOLOAD(cctx, auto_name, &t_any);
vim_free(auto_name); vim_free(auto_name);
@ -329,7 +329,7 @@ compile_load_scriptvar(
char_u sid_name[MAX_FUNC_NAME_LEN]; char_u sid_name[MAX_FUNC_NAME_LEN];
func_name_with_sid(exp_name, import->imp_sid, sid_name); func_name_with_sid(exp_name, import->imp_sid, sid_name);
res = generate_PUSHFUNC(cctx, sid_name, &t_func_any); res = generate_PUSHFUNC(cctx, sid_name, &t_func_any, TRUE);
} }
else else
res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name, res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name,
@ -353,7 +353,7 @@ compile_load_scriptvar(
if (ufunc != NULL) if (ufunc != NULL)
{ {
// function call or function reference // function call or function reference
generate_PUSHFUNC(cctx, ufunc->uf_name, NULL); generate_PUSHFUNC(cctx, ufunc->uf_name, NULL, TRUE);
return OK; return OK;
} }
return FAIL; return FAIL;
@ -387,7 +387,7 @@ generate_funcref(cctx_T *cctx, char_u *name, int has_g_prefix)
if (func_needs_compiling(ufunc, compile_type) if (func_needs_compiling(ufunc, compile_type)
&& compile_def_function(ufunc, TRUE, compile_type, NULL) == FAIL) && compile_def_function(ufunc, TRUE, compile_type, NULL) == FAIL)
return FAIL; return FAIL;
return generate_PUSHFUNC(cctx, ufunc->uf_name, ufunc->uf_func_type); return generate_PUSHFUNC(cctx, ufunc->uf_name, ufunc->uf_func_type, TRUE);
} }
/* /*
@ -609,20 +609,11 @@ compile_string(isn_T *isn, cctx_T *cctx, int str_offset)
return OK; return OK;
} }
/*
* List of special functions for "compile_arguments".
*/
typedef enum {
CA_NOT_SPECIAL,
CA_SEARCHPAIR, // {skip} in searchpair() and searchpairpos()
CA_SUBSTITUTE, // {sub} in substitute(), when prefixed with \=
} ca_special_T;
/* /*
* Compile the argument expressions. * Compile the argument expressions.
* "arg" points to just after the "(" and is advanced to after the ")" * "arg" points to just after the "(" and is advanced to after the ")"
*/ */
static int int
compile_arguments( compile_arguments(
char_u **arg, char_u **arg,
cctx_T *cctx, cctx_T *cctx,

View File

@ -616,7 +616,7 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
case VAR_FUNC: case VAR_FUNC:
if (tv->vval.v_string != NULL) if (tv->vval.v_string != NULL)
iemsg("non-null function constant not supported"); iemsg("non-null function constant not supported");
generate_PUSHFUNC(cctx, NULL, &t_func_unknown); generate_PUSHFUNC(cctx, NULL, &t_func_unknown, TRUE);
break; break;
case VAR_PARTIAL: case VAR_PARTIAL:
if (tv->vval.v_partial != NULL) if (tv->vval.v_partial != NULL)
@ -796,9 +796,11 @@ generate_PUSHBLOB(cctx_T *cctx, blob_T *blob)
/* /*
* Generate an ISN_PUSHFUNC instruction with name "name". * Generate an ISN_PUSHFUNC instruction with name "name".
* When "may_prefix" is TRUE prefix "g:" unless "name" is script-local or
* autoload.
*/ */
int int
generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type) generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type, int may_prefix)
{ {
isn_T *isn; isn_T *isn;
char_u *funcname; char_u *funcname;
@ -808,7 +810,8 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
return FAIL; return FAIL;
if (name == NULL) if (name == NULL)
funcname = NULL; funcname = NULL;
else if (*name == K_SPECIAL // script-local else if (!may_prefix
|| *name == K_SPECIAL // script-local
|| vim_strchr(name, AUTOLOAD_CHAR) != NULL) // autoload || vim_strchr(name, AUTOLOAD_CHAR) != NULL) // autoload
funcname = vim_strsave(name); funcname = vim_strsave(name);
else else
@ -1678,6 +1681,22 @@ generate_PCALL(
return OK; return OK;
} }
/*
* Generate an ISN_DEFER instruction.
*/
int
generate_DEFER(cctx_T *cctx, int var_idx, int argcount)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_drop(cctx, ISN_DEFER, argcount + 1)) == NULL)
return FAIL;
isn->isn_arg.defer.defer_var_idx = var_idx;
isn->isn_arg.defer.defer_argcount = argcount;
return OK;
}
/* /*
* Generate an ISN_STRINGMEMBER instruction. * Generate an ISN_STRINGMEMBER instruction.
*/ */
@ -2240,6 +2259,7 @@ delete_instr(isn_T *isn)
case ISN_CONCAT: case ISN_CONCAT:
case ISN_COND2BOOL: case ISN_COND2BOOL:
case ISN_DEBUG: case ISN_DEBUG:
case ISN_DEFER:
case ISN_DROP: case ISN_DROP:
case ISN_ECHO: case ISN_ECHO:
case ISN_ECHOCONSOLE: case ISN_ECHOCONSOLE:
@ -2310,7 +2330,7 @@ delete_instr(isn_T *isn)
case ISN_USEDICT: case ISN_USEDICT:
// nothing allocated // nothing allocated
break; break;
} }
} }
void void