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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20230127201432.32BD81C0559%40moolenaar.net.

Raspunde prin e-mail lui