Skip to content

Commit 8dbab1d

Browse files
committed
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 vim#11886)
1 parent 657aea7 commit 8dbab1d

File tree

8 files changed

+99
-21
lines changed

8 files changed

+99
-21
lines changed

src/proto/vim9instr.pro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
55
isn_T *generate_instr_debug(cctx_T *cctx);
66
int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
77
int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
8-
int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int itf_idx, type_T *type);
8+
int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type);
99
int generate_STORE_THIS(cctx_T *cctx, int idx);
1010
int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
1111
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);
6161
int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
6262
int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
6363
int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int at_top);
64-
int generate_DEFER(cctx_T *cctx, int var_idx, int argcount);
64+
int generate_DEFER(cctx_T *cctx, int var_idx, int obj_method, int argcount);
6565
int generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len);
6666
int generate_ECHO(cctx_T *cctx, int with_white, int count);
6767
int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count);

src/testdir/test_vim9_class.vim

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,5 +1331,35 @@ def Test_closure_in_class()
13311331
v9.CheckScriptSuccess(lines)
13321332
enddef
13331333

1334+
def Test_defer_with_object()
1335+
var lines =<< trim END
1336+
vim9script
1337+
1338+
class CWithEE
1339+
def Enter()
1340+
g:result ..= "entered/"
1341+
enddef
1342+
def Exit()
1343+
g:result ..= "exited"
1344+
enddef
1345+
endclass
1346+
1347+
def With(ee: CWithEE, F: func)
1348+
ee.Enter()
1349+
defer ee.Exit()
1350+
F()
1351+
enddef
1352+
1353+
g:result = ''
1354+
var obj = CWithEE.new()
1355+
obj->With(() => {
1356+
g:result ..= "called/"
1357+
})
1358+
assert_equal('entered/called/exited', g:result)
1359+
END
1360+
v9.CheckScriptSuccess(lines)
1361+
unlet g:result
1362+
enddef
1363+
13341364

13351365
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ static char *(features[]) =
695695

696696
static int included_patches[] =
697697
{ /* Add new patch number below this line */
698+
/**/
699+
1250,
698700
/**/
699701
1249,
700702
/**/

src/vim9.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ typedef enum {
122122
ISN_NEWFUNC, // create a global function from a lambda function
123123
ISN_DEF, // list functions
124124
ISN_DEFER, // :defer argument count is isn_arg.number
125+
ISN_DEFEROBJ, // idem, function is an object method
125126

126127
// expression operations
127128
ISN_JUMP, // jump if condition is matched isn_arg.jump

src/vim9cmds.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,6 +1910,7 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
19101910
int defer_var_idx;
19111911
type_T *type;
19121912
int func_idx;
1913+
int obj_method = 0;
19131914

19141915
// Get a funcref for the function name.
19151916
// TODO: better way to find the "(".
@@ -1925,8 +1926,15 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
19251926
// TODO: better type
19261927
generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx),
19271928
&t_func_any, FALSE);
1928-
else if (compile_expr0(&arg, cctx) == FAIL)
1929-
return NULL;
1929+
else
1930+
{
1931+
int typecount = cctx->ctx_type_stack.ga_len;
1932+
if (compile_expr0(&arg, cctx) == FAIL)
1933+
return NULL;
1934+
if (cctx->ctx_type_stack.ga_len >= typecount + 2)
1935+
// must have seen "obj.Func", pushed an object and a function
1936+
obj_method = 1;
1937+
}
19301938
*paren = '(';
19311939

19321940
// check for function type
@@ -1958,7 +1966,7 @@ compile_defer(char_u *arg_start, cctx_T *cctx)
19581966
defer_var_idx = get_defer_var_idx(cctx);
19591967
if (defer_var_idx == 0)
19601968
return NULL;
1961-
if (generate_DEFER(cctx, defer_var_idx - 1, argcount) == FAIL)
1969+
if (generate_DEFER(cctx, defer_var_idx - 1, obj_method, argcount) == FAIL)
19621970
return NULL;
19631971

19641972
return skipwhite(arg);

src/vim9execute.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -973,33 +973,36 @@ add_defer_item(int var_idx, int argcount, ectx_T *ectx)
973973
* Returns OK or FAIL.
974974
*/
975975
static int
976-
defer_command(int var_idx, int argcount, ectx_T *ectx)
976+
defer_command(int var_idx, int has_obj, int argcount, ectx_T *ectx)
977977
{
978-
list_T *l = add_defer_item(var_idx, argcount, ectx);
978+
int obj_off = has_obj ? 1 : 0;
979+
list_T *l = add_defer_item(var_idx, argcount + obj_off, ectx);
979980
int i;
980981
typval_T *func_tv;
981982

982983
if (l == NULL)
983984
return FAIL;
984985

985986
func_tv = STACK_TV_BOT(-argcount - 1);
986-
if (func_tv->v_type != VAR_FUNC && func_tv->v_type != VAR_PARTIAL)
987+
if (has_obj ? func_tv->v_type != VAR_PARTIAL : func_tv->v_type != VAR_FUNC)
987988
{
988989
semsg(_(e_expected_str_but_got_str),
989-
"function or partial",
990+
has_obj ? "partial" : "function",
990991
vartype_name(func_tv->v_type));
991992
return FAIL;
992993
}
993994
list_set_item(l, 0, func_tv);
995+
if (has_obj)
996+
list_set_item(l, 1, STACK_TV_BOT(-argcount - 2));
994997

995998
for (i = 0; i < argcount; ++i)
996-
list_set_item(l, i + 1, STACK_TV_BOT(-argcount + i));
997-
ectx->ec_stack.ga_len -= argcount + 1;
999+
list_set_item(l, i + 1 + obj_off, STACK_TV_BOT(-argcount + i));
1000+
ectx->ec_stack.ga_len -= argcount + 1 + obj_off;
9981001
return OK;
9991002
}
10001003

10011004
/*
1002-
* Add a deferred function "name" with one argument "arg_tv".
1005+
* Add a deferred call for "name" with arguments "argvars[argcount]".
10031006
* Consumes "name", also on failure.
10041007
* Only to be called when in_def_function() returns TRUE.
10051008
*/
@@ -1056,19 +1059,31 @@ invoke_defer_funcs(ectx_T *ectx)
10561059
typval_T argvars[MAX_FUNC_ARGS];
10571060
int i;
10581061
listitem_T *arg_li = l->lv_first;
1059-
funcexe_T funcexe;
1062+
typval_T *functv = &l->lv_first->li_tv;
1063+
int obj_off = functv->v_type == VAR_PARTIAL ? 1 : 0;
1064+
int argcount = l->lv_len - 1 - obj_off;
10601065

1061-
for (i = 0; i < l->lv_len - 1; ++i)
1066+
if (obj_off == 1)
1067+
arg_li = arg_li->li_next; // second list item is the object
1068+
for (i = 0; i < argcount; ++i)
10621069
{
10631070
arg_li = arg_li->li_next;
10641071
argvars[i] = arg_li->li_tv;
10651072
}
10661073

1074+
funcexe_T funcexe;
10671075
CLEAR_FIELD(funcexe);
10681076
funcexe.fe_evaluate = TRUE;
10691077
rettv.v_type = VAR_UNKNOWN;
1070-
(void)call_func(l->lv_first->li_tv.vval.v_string, -1,
1071-
&rettv, l->lv_len - 1, argvars, &funcexe);
1078+
if (functv->v_type == VAR_PARTIAL)
1079+
{
1080+
funcexe.fe_partial = functv->vval.v_partial;
1081+
funcexe.fe_object = l->lv_first->li_next->li_tv.vval.v_object;
1082+
if (funcexe.fe_object != NULL)
1083+
++funcexe.fe_object->obj_refcount;
1084+
}
1085+
(void)call_func(functv->vval.v_string, -1,
1086+
&rettv, argcount, argvars, &funcexe);
10721087
clear_tv(&rettv);
10731088
}
10741089
}
@@ -4170,7 +4185,9 @@ exec_instructions(ectx_T *ectx)
41704185

41714186
// :defer func(arg)
41724187
case ISN_DEFER:
4188+
case ISN_DEFEROBJ:
41734189
if (defer_command(iptr->isn_arg.defer.defer_var_idx,
4190+
iptr->isn_type == ISN_DEFEROBJ,
41744191
iptr->isn_arg.defer.defer_argcount, ectx) == FAIL)
41754192
goto on_error;
41764193
break;
@@ -6640,7 +6657,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
66406657
smsg("%s%4d PCALL end", pfx, current);
66416658
break;
66426659
case ISN_DEFER:
6643-
smsg("%s%4d DEFER %d args", pfx, current,
6660+
case ISN_DEFEROBJ:
6661+
smsg("%s%4d %s %d args", pfx, current,
6662+
iptr->isn_type == ISN_DEFER ? "DEFER" : "DEFEROBJ",
66446663
(int)iptr->isn_arg.defer.defer_argcount);
66456664
break;
66466665
case ISN_RETURN:

src/vim9expr.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,17 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
370370
}
371371
}
372372

373+
// Could be a function reference: "obj.Func".
374+
for (int i = 0; i < cl->class_obj_method_count; ++i)
375+
{
376+
ufunc_T *fp = cl->class_obj_methods[i];
377+
// Use a separate pointer to avoid that ASAN complains about
378+
// uf_name[] only being 4 characters.
379+
char_u *ufname = (char_u *)fp->uf_name;
380+
if (STRNCMP(name, ufname, len) == 0 && ufname[len] == NUL)
381+
return generate_FUNCREF(cctx, fp, NULL);
382+
}
383+
373384
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
374385
}
375386
else

src/vim9instr.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,8 +1329,6 @@ generate_NEWDICT(cctx_T *cctx, int count, int use_null)
13291329
/*
13301330
* Generate an ISN_FUNCREF instruction.
13311331
* "isnp" is set to the instruction, so that fr_dfunc_idx can be set later.
1332-
* If variables were declared inside a loop "loop_var_idx" is the index of the
1333-
* first one and "loop_var_count" the number of variables declared.
13341332
*/
13351333
int
13361334
generate_FUNCREF(
@@ -1362,7 +1360,12 @@ generate_FUNCREF(
13621360
if (ufunc->uf_def_status == UF_NOT_COMPILED)
13631361
extra->fre_func_name = vim_strsave(ufunc->uf_name);
13641362
else
1363+
{
1364+
if (isnp == NULL && ufunc->uf_def_status == UF_TO_BE_COMPILED)
1365+
// compile the function now, we need the uf_dfunc_idx value
1366+
(void)compile_def_function(ufunc, FALSE, CT_NONE, NULL);
13651367
isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
1368+
}
13661369

13671370
// Reserve an extra variable to keep track of the number of closures
13681371
// created.
@@ -1942,14 +1945,17 @@ generate_PCALL(
19421945

19431946
/*
19441947
* Generate an ISN_DEFER instruction.
1948+
* "obj_method" is one for "obj.Method()", zero otherwise.
19451949
*/
19461950
int
1947-
generate_DEFER(cctx_T *cctx, int var_idx, int argcount)
1951+
generate_DEFER(cctx_T *cctx, int var_idx, int obj_method, int argcount)
19481952
{
19491953
isn_T *isn;
19501954

19511955
RETURN_OK_IF_SKIP(cctx);
1952-
if ((isn = generate_instr_drop(cctx, ISN_DEFER, argcount + 1)) == NULL)
1956+
if ((isn = generate_instr_drop(cctx,
1957+
obj_method == 0 ? ISN_DEFER : ISN_DEFEROBJ,
1958+
argcount + 1)) == NULL)
19531959
return FAIL;
19541960
isn->isn_arg.defer.defer_var_idx = var_idx;
19551961
isn->isn_arg.defer.defer_argcount = argcount;
@@ -2568,6 +2574,7 @@ delete_instr(isn_T *isn)
25682574
case ISN_COND2BOOL:
25692575
case ISN_DEBUG:
25702576
case ISN_DEFER:
2577+
case ISN_DEFEROBJ:
25712578
case ISN_DROP:
25722579
case ISN_ECHO:
25732580
case ISN_ECHOCONSOLE:

0 commit comments

Comments
 (0)