mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.0.1250: cannot use an object method with :defer
Problem: Cannot use an object method with :defer. (Ernie Rael) Solution: Find the object method and generate code to call it. (closes #11886)
This commit is contained in:
@@ -5,7 +5,7 @@ isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
|
|||||||
isn_T *generate_instr_debug(cctx_T *cctx);
|
isn_T *generate_instr_debug(cctx_T *cctx);
|
||||||
int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
|
int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
|
||||||
int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
|
int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
|
||||||
int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int itf_idx, type_T *type);
|
int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type);
|
||||||
int generate_STORE_THIS(cctx_T *cctx, int idx);
|
int generate_STORE_THIS(cctx_T *cctx, int idx);
|
||||||
int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
|
int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
|
||||||
int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
|
int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
|
||||||
@@ -61,7 +61,7 @@ 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 check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
|
int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
|
||||||
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_DEFER(cctx_T *cctx, int var_idx, int obj_method, 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);
|
||||||
|
@@ -1331,5 +1331,35 @@ def Test_closure_in_class()
|
|||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_defer_with_object()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class CWithEE
|
||||||
|
def Enter()
|
||||||
|
g:result ..= "entered/"
|
||||||
|
enddef
|
||||||
|
def Exit()
|
||||||
|
g:result ..= "exited"
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def With(ee: CWithEE, F: func)
|
||||||
|
ee.Enter()
|
||||||
|
defer ee.Exit()
|
||||||
|
F()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
g:result = ''
|
||||||
|
var obj = CWithEE.new()
|
||||||
|
obj->With(() => {
|
||||||
|
g:result ..= "called/"
|
||||||
|
})
|
||||||
|
assert_equal('entered/called/exited', g:result)
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
unlet g:result
|
||||||
|
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
|
||||||
|
@@ -695,6 +695,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 */
|
||||||
|
/**/
|
||||||
|
1250,
|
||||||
/**/
|
/**/
|
||||||
1249,
|
1249,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -122,6 +122,7 @@ typedef enum {
|
|||||||
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
|
ISN_DEFER, // :defer argument count is isn_arg.number
|
||||||
|
ISN_DEFEROBJ, // idem, function is an object method
|
||||||
|
|
||||||
// expression operations
|
// expression operations
|
||||||
ISN_JUMP, // jump if condition is matched isn_arg.jump
|
ISN_JUMP, // jump if condition is matched isn_arg.jump
|
||||||
|
@@ -1910,6 +1910,7 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
|
|||||||
int defer_var_idx;
|
int defer_var_idx;
|
||||||
type_T *type;
|
type_T *type;
|
||||||
int func_idx;
|
int func_idx;
|
||||||
|
int obj_method = 0;
|
||||||
|
|
||||||
// Get a funcref for the function name.
|
// Get a funcref for the function name.
|
||||||
// TODO: better way to find the "(".
|
// TODO: better way to find the "(".
|
||||||
@@ -1925,8 +1926,15 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
|
|||||||
// TODO: better type
|
// TODO: better type
|
||||||
generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx),
|
generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx),
|
||||||
&t_func_any, FALSE);
|
&t_func_any, FALSE);
|
||||||
else if (compile_expr0(&arg, cctx) == FAIL)
|
else
|
||||||
return NULL;
|
{
|
||||||
|
int typecount = cctx->ctx_type_stack.ga_len;
|
||||||
|
if (compile_expr0(&arg, cctx) == FAIL)
|
||||||
|
return NULL;
|
||||||
|
if (cctx->ctx_type_stack.ga_len >= typecount + 2)
|
||||||
|
// must have seen "obj.Func", pushed an object and a function
|
||||||
|
obj_method = 1;
|
||||||
|
}
|
||||||
*paren = '(';
|
*paren = '(';
|
||||||
|
|
||||||
// check for function type
|
// check for function type
|
||||||
@@ -1958,7 +1966,7 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
|
|||||||
defer_var_idx = get_defer_var_idx(cctx);
|
defer_var_idx = get_defer_var_idx(cctx);
|
||||||
if (defer_var_idx == 0)
|
if (defer_var_idx == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (generate_DEFER(cctx, defer_var_idx - 1, argcount) == FAIL)
|
if (generate_DEFER(cctx, defer_var_idx - 1, obj_method, argcount) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return skipwhite(arg);
|
return skipwhite(arg);
|
||||||
|
@@ -973,9 +973,10 @@ add_defer_item(int var_idx, int argcount, ectx_T *ectx)
|
|||||||
* Returns OK or FAIL.
|
* Returns OK or FAIL.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
defer_command(int var_idx, int argcount, ectx_T *ectx)
|
defer_command(int var_idx, int has_obj, int argcount, ectx_T *ectx)
|
||||||
{
|
{
|
||||||
list_T *l = add_defer_item(var_idx, argcount, ectx);
|
int obj_off = has_obj ? 1 : 0;
|
||||||
|
list_T *l = add_defer_item(var_idx, argcount + obj_off, ectx);
|
||||||
int i;
|
int i;
|
||||||
typval_T *func_tv;
|
typval_T *func_tv;
|
||||||
|
|
||||||
@@ -983,23 +984,25 @@ defer_command(int var_idx, int argcount, ectx_T *ectx)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
func_tv = STACK_TV_BOT(-argcount - 1);
|
func_tv = STACK_TV_BOT(-argcount - 1);
|
||||||
if (func_tv->v_type != VAR_FUNC && func_tv->v_type != VAR_PARTIAL)
|
if (has_obj ? func_tv->v_type != VAR_PARTIAL : func_tv->v_type != VAR_FUNC)
|
||||||
{
|
{
|
||||||
semsg(_(e_expected_str_but_got_str),
|
semsg(_(e_expected_str_but_got_str),
|
||||||
"function or partial",
|
has_obj ? "partial" : "function",
|
||||||
vartype_name(func_tv->v_type));
|
vartype_name(func_tv->v_type));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
list_set_item(l, 0, func_tv);
|
list_set_item(l, 0, func_tv);
|
||||||
|
if (has_obj)
|
||||||
|
list_set_item(l, 1, STACK_TV_BOT(-argcount - 2));
|
||||||
|
|
||||||
for (i = 0; i < argcount; ++i)
|
for (i = 0; i < argcount; ++i)
|
||||||
list_set_item(l, i + 1, STACK_TV_BOT(-argcount + i));
|
list_set_item(l, i + 1 + obj_off, STACK_TV_BOT(-argcount + i));
|
||||||
ectx->ec_stack.ga_len -= argcount + 1;
|
ectx->ec_stack.ga_len -= argcount + 1 + obj_off;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a deferred function "name" with one argument "arg_tv".
|
* Add a deferred call for "name" with arguments "argvars[argcount]".
|
||||||
* Consumes "name", also on failure.
|
* Consumes "name", also on failure.
|
||||||
* Only to be called when in_def_function() returns TRUE.
|
* Only to be called when in_def_function() returns TRUE.
|
||||||
*/
|
*/
|
||||||
@@ -1056,19 +1059,31 @@ invoke_defer_funcs(ectx_T *ectx)
|
|||||||
typval_T argvars[MAX_FUNC_ARGS];
|
typval_T argvars[MAX_FUNC_ARGS];
|
||||||
int i;
|
int i;
|
||||||
listitem_T *arg_li = l->lv_first;
|
listitem_T *arg_li = l->lv_first;
|
||||||
funcexe_T funcexe;
|
typval_T *functv = &l->lv_first->li_tv;
|
||||||
|
int obj_off = functv->v_type == VAR_PARTIAL ? 1 : 0;
|
||||||
|
int argcount = l->lv_len - 1 - obj_off;
|
||||||
|
|
||||||
for (i = 0; i < l->lv_len - 1; ++i)
|
if (obj_off == 1)
|
||||||
|
arg_li = arg_li->li_next; // second list item is the object
|
||||||
|
for (i = 0; i < argcount; ++i)
|
||||||
{
|
{
|
||||||
arg_li = arg_li->li_next;
|
arg_li = arg_li->li_next;
|
||||||
argvars[i] = arg_li->li_tv;
|
argvars[i] = arg_li->li_tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
funcexe_T funcexe;
|
||||||
CLEAR_FIELD(funcexe);
|
CLEAR_FIELD(funcexe);
|
||||||
funcexe.fe_evaluate = TRUE;
|
funcexe.fe_evaluate = TRUE;
|
||||||
rettv.v_type = VAR_UNKNOWN;
|
rettv.v_type = VAR_UNKNOWN;
|
||||||
(void)call_func(l->lv_first->li_tv.vval.v_string, -1,
|
if (functv->v_type == VAR_PARTIAL)
|
||||||
&rettv, l->lv_len - 1, argvars, &funcexe);
|
{
|
||||||
|
funcexe.fe_partial = functv->vval.v_partial;
|
||||||
|
funcexe.fe_object = l->lv_first->li_next->li_tv.vval.v_object;
|
||||||
|
if (funcexe.fe_object != NULL)
|
||||||
|
++funcexe.fe_object->obj_refcount;
|
||||||
|
}
|
||||||
|
(void)call_func(functv->vval.v_string, -1,
|
||||||
|
&rettv, argcount, argvars, &funcexe);
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4170,7 +4185,9 @@ exec_instructions(ectx_T *ectx)
|
|||||||
|
|
||||||
// :defer func(arg)
|
// :defer func(arg)
|
||||||
case ISN_DEFER:
|
case ISN_DEFER:
|
||||||
|
case ISN_DEFEROBJ:
|
||||||
if (defer_command(iptr->isn_arg.defer.defer_var_idx,
|
if (defer_command(iptr->isn_arg.defer.defer_var_idx,
|
||||||
|
iptr->isn_type == ISN_DEFEROBJ,
|
||||||
iptr->isn_arg.defer.defer_argcount, ectx) == FAIL)
|
iptr->isn_arg.defer.defer_argcount, ectx) == FAIL)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
break;
|
break;
|
||||||
@@ -6640,7 +6657,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
smsg("%s%4d PCALL end", pfx, current);
|
smsg("%s%4d PCALL end", pfx, current);
|
||||||
break;
|
break;
|
||||||
case ISN_DEFER:
|
case ISN_DEFER:
|
||||||
smsg("%s%4d DEFER %d args", pfx, current,
|
case ISN_DEFEROBJ:
|
||||||
|
smsg("%s%4d %s %d args", pfx, current,
|
||||||
|
iptr->isn_type == ISN_DEFER ? "DEFER" : "DEFEROBJ",
|
||||||
(int)iptr->isn_arg.defer.defer_argcount);
|
(int)iptr->isn_arg.defer.defer_argcount);
|
||||||
break;
|
break;
|
||||||
case ISN_RETURN:
|
case ISN_RETURN:
|
||||||
|
@@ -370,6 +370,17 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Could be a function reference: "obj.Func".
|
||||||
|
for (int i = 0; i < cl->class_obj_method_count; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *fp = cl->class_obj_methods[i];
|
||||||
|
// Use a separate pointer to avoid that ASAN complains about
|
||||||
|
// uf_name[] only being 4 characters.
|
||||||
|
char_u *ufname = (char_u *)fp->uf_name;
|
||||||
|
if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
|
||||||
|
return generate_FUNCREF(cctx, fp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
|
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -1329,8 +1329,6 @@ 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(
|
generate_FUNCREF(
|
||||||
@@ -1362,7 +1360,12 @@ generate_FUNCREF(
|
|||||||
if (ufunc->uf_def_status == UF_NOT_COMPILED)
|
if (ufunc->uf_def_status == UF_NOT_COMPILED)
|
||||||
extra->fre_func_name = vim_strsave(ufunc->uf_name);
|
extra->fre_func_name = vim_strsave(ufunc->uf_name);
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (isnp == NULL && ufunc->uf_def_status == UF_TO_BE_COMPILED)
|
||||||
|
// compile the function now, we need the uf_dfunc_idx value
|
||||||
|
(void)compile_def_function(ufunc, FALSE, CT_NONE, NULL);
|
||||||
isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
|
isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
|
||||||
|
}
|
||||||
|
|
||||||
// Reserve an extra variable to keep track of the number of closures
|
// Reserve an extra variable to keep track of the number of closures
|
||||||
// created.
|
// created.
|
||||||
@@ -1942,14 +1945,17 @@ generate_PCALL(
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_DEFER instruction.
|
* Generate an ISN_DEFER instruction.
|
||||||
|
* "obj_method" is one for "obj.Method()", zero otherwise.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
generate_DEFER(cctx_T *cctx, int var_idx, int argcount)
|
generate_DEFER(cctx_T *cctx, int var_idx, int obj_method, int argcount)
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
|
|
||||||
RETURN_OK_IF_SKIP(cctx);
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
if ((isn = generate_instr_drop(cctx, ISN_DEFER, argcount + 1)) == NULL)
|
if ((isn = generate_instr_drop(cctx,
|
||||||
|
obj_method == 0 ? ISN_DEFER : ISN_DEFEROBJ,
|
||||||
|
argcount + 1)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
isn->isn_arg.defer.defer_var_idx = var_idx;
|
isn->isn_arg.defer.defer_var_idx = var_idx;
|
||||||
isn->isn_arg.defer.defer_argcount = argcount;
|
isn->isn_arg.defer.defer_argcount = argcount;
|
||||||
@@ -2568,6 +2574,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_COND2BOOL:
|
case ISN_COND2BOOL:
|
||||||
case ISN_DEBUG:
|
case ISN_DEBUG:
|
||||||
case ISN_DEFER:
|
case ISN_DEFER:
|
||||||
|
case ISN_DEFEROBJ:
|
||||||
case ISN_DROP:
|
case ISN_DROP:
|
||||||
case ISN_ECHO:
|
case ISN_ECHO:
|
||||||
case ISN_ECHOCONSOLE:
|
case ISN_ECHOCONSOLE:
|
||||||
|
Reference in New Issue
Block a user