To: vim_dev@googlegroups.com Subject: Patch 9.0.1250 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1250 Problem: Cannot use an object method with :defer. (Ernie Rael) Solution: Find the object method and generate code to call it. (closes #11886) Files: src/vim9expr.c, src/vim9cmds.c, src/vim9instr.c, src/vim9.h, src/proto/vim9instr.pro, src/vim9execute.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1249/src/vim9expr.c 2023-01-24 15:07:00.424562535 +0000 --- src/vim9expr.c 2023-01-27 18:15:21.396047640 +0000 *************** *** 370,375 **** --- 370,386 ---- } } + // 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); } else *** ../vim-9.0.1249/src/vim9cmds.c 2023-01-02 18:10:00.023271223 +0000 --- src/vim9cmds.c 2023-01-27 19:12:07.742958484 +0000 *************** *** 1910,1915 **** --- 1910,1916 ---- int defer_var_idx; type_T *type; int func_idx; + int obj_method = 0; // Get a funcref for the function name. // TODO: better way to find the "(". *************** *** 1925,1932 **** // 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; *paren = '('; // check for function type --- 1926,1940 ---- // TODO: better type generate_PUSHFUNC(cctx, (char_u *)internal_func_name(func_idx), &t_func_any, FALSE); ! else ! { ! 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 = '('; // check for function type *************** *** 1958,1964 **** defer_var_idx = get_defer_var_idx(cctx); if (defer_var_idx == 0) return NULL; ! if (generate_DEFER(cctx, defer_var_idx - 1, argcount) == FAIL) return NULL; return skipwhite(arg); --- 1966,1972 ---- defer_var_idx = get_defer_var_idx(cctx); if (defer_var_idx == 0) return NULL; ! if (generate_DEFER(cctx, defer_var_idx - 1, obj_method, argcount) == FAIL) return NULL; return skipwhite(arg); *** ../vim-9.0.1249/src/vim9instr.c 2023-01-26 11:58:39.606071598 +0000 --- src/vim9instr.c 2023-01-27 18:59:23.863206936 +0000 *************** *** 1329,1336 **** /* * Generate an ISN_FUNCREF instruction. * "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 generate_FUNCREF( --- 1329,1334 ---- *************** *** 1362,1368 **** --- 1360,1371 ---- if (ufunc->uf_def_status == UF_NOT_COMPILED) extra->fre_func_name = vim_strsave(ufunc->uf_name); 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; + } // Reserve an extra variable to keep track of the number of closures // created. *************** *** 1942,1955 **** /* * 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; --- 1945,1961 ---- /* * Generate an ISN_DEFER instruction. + * "obj_method" is one for "obj.Method()", zero otherwise. */ int ! generate_DEFER(cctx_T *cctx, int var_idx, int obj_method, int argcount) { isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if ((isn = generate_instr_drop(cctx, ! obj_method == 0 ? ISN_DEFER : ISN_DEFEROBJ, ! argcount + 1)) == NULL) return FAIL; isn->isn_arg.defer.defer_var_idx = var_idx; isn->isn_arg.defer.defer_argcount = argcount; *************** *** 2568,2573 **** --- 2574,2580 ---- case ISN_COND2BOOL: case ISN_DEBUG: case ISN_DEFER: + case ISN_DEFEROBJ: case ISN_DROP: case ISN_ECHO: case ISN_ECHOCONSOLE: *** ../vim-9.0.1249/src/vim9.h 2023-01-16 20:47:53.889287080 +0000 --- src/vim9.h 2023-01-27 18:23:30.299910799 +0000 *************** *** 122,127 **** --- 122,128 ---- ISN_NEWFUNC, // create a global function from a lambda function ISN_DEF, // list functions ISN_DEFER, // :defer argument count is isn_arg.number + ISN_DEFEROBJ, // idem, function is an object method // expression operations ISN_JUMP, // jump if condition is matched isn_arg.jump *** ../vim-9.0.1249/src/proto/vim9instr.pro 2023-01-16 19:43:43.386873678 +0000 --- src/proto/vim9instr.pro 2023-01-27 18:22:47.859920145 +0000 *************** *** 5,11 **** isn_T *generate_instr_debug(cctx_T *cctx); 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_ITF_MEMBER(cctx_T *cctx, class_T *itf, int itf_idx, type_T *type); int generate_STORE_THIS(cctx_T *cctx, int idx); 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); --- 5,11 ---- isn_T *generate_instr_debug(cctx_T *cctx); 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_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type); int generate_STORE_THIS(cctx_T *cctx, int idx); 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); *************** *** 61,67 **** 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 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_ECHO(cctx_T *cctx, int with_white, int count); int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count); --- 61,67 ---- 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 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 obj_method, int argcount); 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_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count); *** ../vim-9.0.1249/src/vim9execute.c 2023-01-26 11:58:39.606071598 +0000 --- src/vim9execute.c 2023-01-27 20:00:32.524817422 +0000 *************** *** 973,981 **** * Returns OK or FAIL. */ static int ! defer_command(int var_idx, int argcount, ectx_T *ectx) { ! list_T *l = add_defer_item(var_idx, argcount, ectx); int i; typval_T *func_tv; --- 973,982 ---- * Returns OK or FAIL. */ static int ! defer_command(int var_idx, int has_obj, int argcount, ectx_T *ectx) { ! int obj_off = has_obj ? 1 : 0; ! list_T *l = add_defer_item(var_idx, argcount + obj_off, ectx); int i; typval_T *func_tv; *************** *** 983,1005 **** return FAIL; func_tv = STACK_TV_BOT(-argcount - 1); ! if (func_tv->v_type != VAR_FUNC && func_tv->v_type != VAR_PARTIAL) { semsg(_(e_expected_str_but_got_str), ! "function or partial", vartype_name(func_tv->v_type)); return FAIL; } list_set_item(l, 0, func_tv); for (i = 0; i < argcount; ++i) ! list_set_item(l, i + 1, STACK_TV_BOT(-argcount + i)); ! ectx->ec_stack.ga_len -= argcount + 1; return OK; } /* ! * Add a deferred function "name" with one argument "arg_tv". * Consumes "name", also on failure. * Only to be called when in_def_function() returns TRUE. */ --- 984,1008 ---- return FAIL; func_tv = STACK_TV_BOT(-argcount - 1); ! if (has_obj ? func_tv->v_type != VAR_PARTIAL : func_tv->v_type != VAR_FUNC) { semsg(_(e_expected_str_but_got_str), ! has_obj ? "partial" : "function", vartype_name(func_tv->v_type)); return FAIL; } 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) ! list_set_item(l, i + 1 + obj_off, STACK_TV_BOT(-argcount + i)); ! ectx->ec_stack.ga_len -= argcount + 1 + obj_off; return OK; } /* ! * Add a deferred call for "name" with arguments "argvars[argcount]". * Consumes "name", also on failure. * Only to be called when in_def_function() returns TRUE. */ *************** *** 1056,1074 **** 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); } } --- 1059,1089 ---- typval_T argvars[MAX_FUNC_ARGS]; int i; listitem_T *arg_li = l->lv_first; ! 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; ! ! 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; argvars[i] = arg_li->li_tv; } + funcexe_T funcexe; CLEAR_FIELD(funcexe); funcexe.fe_evaluate = TRUE; rettv.v_type = VAR_UNKNOWN; ! if (functv->v_type == VAR_PARTIAL) ! { ! 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); } } *************** *** 4170,4176 **** --- 4185,4193 ---- // :defer func(arg) case ISN_DEFER: + case ISN_DEFEROBJ: if (defer_command(iptr->isn_arg.defer.defer_var_idx, + iptr->isn_type == ISN_DEFEROBJ, iptr->isn_arg.defer.defer_argcount, ectx) == FAIL) goto on_error; break; *************** *** 6640,6646 **** 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: --- 6657,6665 ---- smsg("%s%4d PCALL end", pfx, current); break; case ISN_DEFER: ! 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); break; case ISN_RETURN: *** ../vim-9.0.1249/src/testdir/test_vim9_class.vim 2023-01-27 13:16:14.674850404 +0000 --- src/testdir/test_vim9_class.vim 2023-01-27 19:45:52.893250746 +0000 *************** *** 1331,1335 **** --- 1331,1365 ---- v9.CheckScriptSuccess(lines) 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-9.0.1249/src/version.c 2023-01-27 13:16:14.674850404 +0000 --- src/version.c 2023-01-27 17:38:03.953326411 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1250, /**/ -- hundred-and-one symptoms of being an internet addict: 55. You ask your doctor to implant a gig in your brain. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///