]> granicus.if.org Git - vim/commitdiff
patch 9.0.0370: cleaning up afterwards can make a function messy v9.0.0370
authorBram Moolenaar <Bram@vim.org>
Sat, 3 Sep 2022 20:35:53 +0000 (21:35 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 3 Sep 2022 20:35:53 +0000 (21:35 +0100)
Problem:    Cleaning up afterwards can make a function messy.
Solution:   Add the :defer command.

19 files changed:
runtime/doc/eval.txt
src/ex_cmdidxs.h
src/ex_cmds.h
src/proto/userfunc.pro
src/proto/vim9cmds.pro
src/proto/vim9expr.pro
src/proto/vim9instr.pro
src/structs.h
src/testdir/test_user_func.vim
src/testdir/test_vim9_disassemble.vim
src/testdir/test_vim9_func.vim
src/userfunc.c
src/version.c
src/vim9.h
src/vim9cmds.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c

index fd293e4cf44763c625a58205edd1306176c3034d..750fc414db3341ec28e3fda88e5e1a649e2d530b 100644 (file)
@@ -2979,6 +2979,62 @@ is used as a method: >
        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 ~
                                                        *autoload-functions*
 When using many or large functions, it's possible to automatically define them
index 065bf056f5ca893a645b492e4c182e62d509e8c9..fd4b1550a20e91e6990544bf3b453fb0868070a0 100644 (file)
@@ -9,28 +9,28 @@ static const unsigned short cmdidxs1[26] =
   /* b */ 21,
   /* c */ 45,
   /* d */ 112,
-  /* e */ 137,
-  /* f */ 166,
-  /* g */ 183,
-  /* h */ 189,
-  /* i */ 199,
-  /* j */ 219,
-  /* k */ 221,
-  /* l */ 226,
-  /* m */ 289,
-  /* n */ 307,
-  /* o */ 327,
-  /* p */ 339,
-  /* q */ 378,
-  /* r */ 381,
-  /* s */ 401,
-  /* t */ 471,
-  /* u */ 517,
-  /* v */ 528,
-  /* w */ 549,
-  /* x */ 563,
-  /* y */ 573,
-  /* z */ 574
+  /* e */ 138,
+  /* f */ 167,
+  /* g */ 184,
+  /* h */ 190,
+  /* i */ 200,
+  /* j */ 220,
+  /* k */ 222,
+  /* l */ 227,
+  /* m */ 290,
+  /* n */ 308,
+  /* o */ 328,
+  /* p */ 340,
+  /* q */ 379,
+  /* r */ 382,
+  /* s */ 402,
+  /* t */ 472,
+  /* u */ 518,
+  /* v */ 529,
+  /* w */ 550,
+  /* x */ 564,
+  /* y */ 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 },
   /* 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 },
-  /* 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 },
   /* 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 },
@@ -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 }
 };
 
-static const int command_count = 591;
+static const int command_count = 592;
index 8da0da1c9a6444aa62b6c57db368b90a30f5a268..8f7c48f8b4b3e82addefc50ad57c68a70d35e5fd 100644 (file)
@@ -467,6 +467,9 @@ EXCMD(CMD_def,              "def",          ex_function,
 EXCMD(CMD_defcompile,  "defcompile",   ex_defcompile,
        EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_TRLBAR|EX_EXTRA,
        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,
        EX_NEEDARG|EX_WORD1|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
index 427d47b241feb192f4afe63425bab5bcf1dbf97e..3e417dda50e5b643f4cc4fd863dec416443db88b 100644 (file)
@@ -58,6 +58,7 @@ void func_ptr_unref(ufunc_T *fp);
 void func_ref(char_u *name);
 void func_ptr_ref(ufunc_T *fp);
 void ex_return(exarg_T *eap);
+void handle_defer(void);
 void ex_call(exarg_T *eap);
 int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv);
 void discard_pending_return(void *rettv);
index 7529d63d628acbd8487ae021c79175d64a2b592b..10fc2c78d23e8b2bebdb864948792c27a05b0593 100644 (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_throw(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_put(char_u *arg, exarg_T *eap, cctx_T *cctx);
 char_u *compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx);
index 233cfa22674bb3f03745052edd880fb380af7c91..f58e3462526e096c1c9580fdaaac275c24696be4 100644 (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_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_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_const_end(char_u *arg);
 int get_lambda_tv_and_compile(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);
index 8a3254b9eed3996613e4cb448a20cb163909347f..b0f3aa476263c895c0a4db6f4a0dcdd6a27e207f 100644 (file)
@@ -23,7 +23,7 @@ int generate_PUSHS(cctx_T *cctx, char_u **str);
 int generate_PUSHCHANNEL(cctx_T *cctx);
 int generate_PUSHJOB(cctx_T *cctx);
 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_GETITEM(cctx_T *cctx, int index, int with_op);
 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_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_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);
index b8627dddf2f1e85d97fd346c8f8a5f013692340d..8fab297b593c715def821daa5191e5d6cbd42a55 100644 (file)
@@ -1753,6 +1753,7 @@ struct funccall_S
     linenr_T   breakpoint;     // next line with breakpoint or zero
     int                dbg_tick;       // debug_tick when breakpoint was set
     int                level;          // top nesting level of executed function
+    garray_T   fc_defer;       // functions to be called on return
 #ifdef FEAT_PROFILE
     proftime_T prof_child;     // time spent in a child
 #endif
@@ -1767,6 +1768,14 @@ struct funccall_S
                                // "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()
  */
@@ -2850,7 +2859,7 @@ struct file_buffer
     int                b_u_synced;     // entry lists are synced
     long       b_u_seq_last;   // last used undo sequence number
     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
     long       b_u_save_nr_cur; // file write nr after which we are now
 
index f6e25e3b5a075d76882cb46ded6bb249f2b18a42..af05812c6bee58a361df9150573eac85597e6807 100644 (file)
@@ -529,4 +529,36 @@ func Test_funcdef_alloc_failure()
   bw!
 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
index 12be15648326ee5e8cda11c16e34d7b74f73f77d..57b385bd5038e36db69751d11be9fffff878f849 100644 (file)
@@ -2900,4 +2900,19 @@ def Test_disassemble_bitshift()
                '10 RETURN void', instr)
 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
index 0fa74df259f2afab998fdf9160cc44a186a24200..c616e1890dd38ee322497a2d5b5a3b46c267b2c3 100644 (file)
@@ -4272,6 +4272,36 @@ def Test_cexpr_errmsg_line_number()
   v9.CheckScriptFailure(lines, 'E777', 2)
 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.
 if has('python3')
   def Test_python3_command()
index 00c17b8c002891549e05260c5332d61c462cd623..c33ec63f9a91415d694ddbb05d8e7f95bbe85735 100644 (file)
@@ -1728,44 +1728,36 @@ emsg_funcname(char *ermsg, char_u *name)
 }
 
 /*
- * Allocate a variable for the result of a function.
- * Return OK or FAIL.
+ * Get function arguments at "*arg" and advance it.
+ * Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount".
  */
-    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
+    static int
+get_func_arguments(
+       char_u      **arg,
+       evalarg_T   *evalarg,
+       int         partial_argc,
+       typval_T    *argvars,
+       int         *argcount)
 {
-    char_u     *argp;
+    char_u     *argp = *arg;
     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);
 
-    /*
-     * Get the arguments.
-     */
-    argp = *arg;
-    while (argcount < MAX_FUNC_ARGS - (funcexe->fe_partial == NULL ? 0
-                                              : funcexe->fe_partial->pt_argc))
+    while (*argcount < MAX_FUNC_ARGS - partial_argc)
     {
        // skip the '(' or ',' and possibly line breaks
        argp = skipwhite_and_linebreak(argp + 1, evalarg);
 
        if (*argp == ')' || *argp == ',' || *argp == NUL)
            break;
-       if (eval1(&argp, &argvars[argcount], evalarg) == FAIL)
+       if (eval1(&argp, &argvars[*argcount], evalarg) == FAIL)
        {
            ret = FAIL;
            break;
        }
-       ++argcount;
+       ++*argcount;
        // The comma should come right after the argument, but this wasn't
        // checked previously, thus only enforce it in Vim9 script.
        if (vim9script)
@@ -1791,11 +1783,41 @@ get_func_tv(
            break;
        }
     }
+
     argp = skipwhite_and_linebreak(argp, evalarg);
     if (*argp == ')')
        ++argp;
     else
        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)
     {
@@ -2884,6 +2906,9 @@ call_user_func(
        do_cmdline(NULL, get_func_line, (void *)fc,
                                     DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
 
+    // Invoke functions added with ":defer".
+    handle_defer();
+
     --RedrawingDisabled;
 
     // when the function was aborted because of an error, return -1
@@ -5457,8 +5482,164 @@ ex_return(exarg_T *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.
+ * ":defer func(arg1, arg2)"    deferred function call.
  */
     void
 ex_call(exarg_T *eap)
@@ -5468,9 +5649,6 @@ ex_call(exarg_T *eap)
     char_u     *name;
     char_u     *tofree;
     int                len;
-    typval_T   rettv;
-    linenr_T   lnum;
-    int                doesrange;
     int                failed = FALSE;
     funcdict_T fudi;
     partial_T  *partial = NULL;
@@ -5482,6 +5660,8 @@ ex_call(exarg_T *eap)
     fill_evalarg_from_eap(&evalarg, eap, eap->skip);
     if (eap->skip)
     {
+       typval_T        rettv;
+
        // trans_function_name() doesn't work well when skipping, use eval0()
        // instead to skip to any following command, e.g. for:
        //   :if 0 | call dict.foo().bar() | endif
@@ -5531,82 +5711,29 @@ ex_call(exarg_T *eap)
        goto end;
     }
 
-    /*
-     * 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)
+    if (eap->cmdidx == CMD_defer)
     {
-       ++emsg_skip;
-       lnum = eap->line2;      // do it once, also with an invalid range
+       arg = startarg;
+       failed = ex_defer_inner(name, &arg, &evalarg) == FAIL;
     }
     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;
-
        CLEAR_FIELD(funcexe);
-       funcexe.fe_firstline = eap->line1;
-       funcexe.fe_lastline = eap->line2;
-       funcexe.fe_doesrange = &doesrange;
-       funcexe.fe_evaluate = !eap->skip;
+       funcexe.fe_check_type = type;
        funcexe.fe_partial = partial;
        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;
-       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;
+       funcexe.fe_evaluate = !eap->skip;
+       failed = ex_call_inner(eap, name, &arg, startarg, &funcexe, &evalarg);
     }
-    if (eap->skip)
-       --emsg_skip;
 
     // 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.
-    if ((!aborting() || did_throw)
-                                 && (!failed || eap->cstack->cs_trylevel > 0))
+    if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 0))
     {
        // Check for trailing illegal characters and a following command.
        arg = skipwhite(arg);
index 9eba6bf33552b3a273c51884f9df5a685a8d52ec..b438a46297ee14815352492bb6bc539e90c4d82a 100644 (file)
@@ -707,6 +707,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    370,
 /**/
     369,
 /**/
index b32dbeab44f8094f91abb4074ef6d6d338ea040c..22a3f3990e6f56aefe81c73bd23dd09dd7ee086b 100644 (file)
@@ -113,6 +113,7 @@ typedef enum {
     ISN_FUNCREF,    // push a function ref to dfunc isn_arg.funcref
     ISN_NEWFUNC,    // create a global function from a lambda function
     ISN_DEF,       // list functions
+    ISN_DEFER,     // :defer  argument count is isn_arg.number
 
     // expression operations
     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
 } 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
  */
@@ -468,6 +475,7 @@ struct isn_S {
        tobool_T            tobool;
        getitem_T           getitem;
        debug_T             debug;
+       deferins_T          defer;
     } isn_arg;
 };
 
@@ -498,6 +506,9 @@ struct dfunc_S {
 
     int                df_varcount;        // number of local variables
     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.
@@ -735,6 +746,15 @@ struct cctx_S {
                                    // 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()
 #define TVTT_DO_MEMBER     1
 #define TVTT_MORE_SPECIFIC  2  // get most specific type for member
index 4ad87bc2645e295d6914dfefab45a469234677a9..9651801ed09eba85eedf21e7dcbc185c1a737f4d 100644 (file)
@@ -1654,6 +1654,9 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
     return p;
 }
 
+/*
+ * Compile an expression or function call.
+ */
     char_u *
 compile_eval(char_u *arg, cctx_T *cctx)
 {
@@ -1681,6 +1684,67 @@ compile_eval(char_u *arg, cctx_T *cctx)
     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 "echomsg expr"
index a785ba98dbc859a6700cb4289b52d95b58e93804..02cbb25df50c287e3d67aa9a15617f4340619377 100644 (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());
                        break;
                    case VAR_FUNC:
-                       r = generate_PUSHFUNC(cctx, NULL, &t_func_void);
+                       r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE);
                        break;
                    case VAR_LIST:
                        r = generate_NEWLIST(cctx, 0, FALSE);
@@ -2748,6 +2748,7 @@ compile_def_function(
            // Was compiled in this mode before: Free old instructions.
            delete_def_function_contents(dfunc, FALSE);
        ga_clear_strings(&dfunc->df_var_names);
+       dfunc->df_defer_var_idx = 0;
     }
     else
     {
@@ -3249,6 +3250,10 @@ compile_def_function(
                    line = compile_eval(p, &cctx);
                    break;
 
+           case CMD_defer:
+                   line = compile_defer(p, &cctx);
+                   break;
+
            case CMD_echo:
            case CMD_echon:
            case CMD_echoconsole:
index a1311ee177e46643c627c956cb804792dc0a428e..3e0a37773880976b27e4a619721aca6fbfd62e63 100644 (file)
@@ -101,9 +101,15 @@ struct ectx_S {
 static garray_T profile_info_ga = {0, 0, sizeof(profinfo_T), 20, NULL};
 #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.
 #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
 to_string_error(vartype_T vartype)
 {
@@ -610,9 +616,6 @@ call_dfunc(
     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.
 static funcstack_T *first_funcstack = NULL;
 
@@ -842,6 +845,89 @@ set_ref_in_funcstacks(int copyID)
     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.
  */
@@ -876,6 +962,9 @@ func_return(ectx_T *ectx)
     }
 #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
     // happen.
     --dfunc->df_ufunc->uf_calls;
@@ -949,8 +1038,6 @@ func_return(ectx_T *ectx)
     return OK;
 }
 
-#undef STACK_TV
-
 /*
  * Prepare arguments and rettv for calling a builtin or user function.
  */
@@ -1732,16 +1819,6 @@ typedef struct subs_expr_S {
     int                subs_status;
 } 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().
 static ectx_T  *debug_context = NULL;
 static int     debug_var_count;
@@ -3670,6 +3747,13 @@ exec_instructions(ectx_T *ectx)
                }
                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
            case ISN_RETURN_VOID:
                if (GA_GROW_FAILS(&ectx->ec_stack, 1))
@@ -5024,6 +5108,14 @@ on_fatal_error:
 done:
     ret = OK;
 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);
     ectx->ec_trylevel_at_start = save_trylevel_at_start;
     return ret;
@@ -5903,6 +5995,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
            case ISN_PCALL_END:
                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:
                smsg("%s%4d RETURN", pfx, current);
                break;
index 370dce315c878edc637bee2d22004c8589544790..18f29b6ae5b93b10bedf5fd1fca0a24593be8cad 100644 (file)
@@ -313,7 +313,7 @@ compile_load_scriptvar(
                // name.  If a '(' follows it must be a function.  Otherwise we
                // don't know, it can be "script.Func".
                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
                    res = generate_AUTOLOAD(cctx, auto_name, &t_any);
                vim_free(auto_name);
@@ -329,7 +329,7 @@ compile_load_scriptvar(
                    char_u sid_name[MAX_FUNC_NAME_LEN];
 
                    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
                    res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name,
@@ -353,7 +353,7 @@ compile_load_scriptvar(
            if (ufunc != NULL)
            {
                // function call or function reference
-               generate_PUSHFUNC(cctx, ufunc->uf_name, NULL);
+               generate_PUSHFUNC(cctx, ufunc->uf_name, NULL, TRUE);
                return OK;
            }
            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)
              && compile_def_function(ufunc, TRUE, compile_type, NULL) == 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;
 }
 
-/*
- * 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.
  * "arg" points to just after the "(" and is advanced to after the ")"
  */
-    static int
+    int
 compile_arguments(
        char_u       **arg,
        cctx_T       *cctx,
index a13516bbb27c41631935a32bf2c41e6106907695..ae4de33f41e37a6b10c4f8aaa41d6791c5d15e27 100644 (file)
@@ -616,7 +616,7 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
        case VAR_FUNC:
            if (tv->vval.v_string != NULL)
                iemsg("non-null function constant not supported");
-           generate_PUSHFUNC(cctx, NULL, &t_func_unknown);
+           generate_PUSHFUNC(cctx, NULL, &t_func_unknown, TRUE);
            break;
        case VAR_PARTIAL:
            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".
+ * When "may_prefix" is TRUE prefix "g:" unless "name" is script-local or
+ * autoload.
  */
     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;
     char_u     *funcname;
@@ -808,7 +810,8 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
        return FAIL;
     if (name == 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
        funcname = vim_strsave(name);
     else
@@ -1678,6 +1681,22 @@ generate_PCALL(
     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.
  */
@@ -2240,6 +2259,7 @@ delete_instr(isn_T *isn)
        case ISN_CONCAT:
        case ISN_COND2BOOL:
        case ISN_DEBUG:
+       case ISN_DEFER:
        case ISN_DROP:
        case ISN_ECHO:
        case ISN_ECHOCONSOLE:
@@ -2296,21 +2316,21 @@ delete_instr(isn_T *isn)
        case ISN_STOREINDEX:
        case ISN_STORENR:
        case ISN_SOURCE:
-    case ISN_STOREOUTER:
-    case ISN_STORERANGE:
-    case ISN_STOREREG:
-    case ISN_STOREV:
-    case ISN_STRINDEX:
-    case ISN_STRSLICE:
-    case ISN_THROW:
-    case ISN_TRYCONT:
-    case ISN_UNLETINDEX:
-    case ISN_UNLETRANGE:
-    case ISN_UNPACK:
-    case ISN_USEDICT:
+       case ISN_STOREOUTER:
+       case ISN_STORERANGE:
+       case ISN_STOREREG:
+       case ISN_STOREV:
+       case ISN_STRINDEX:
+       case ISN_STRSLICE:
+       case ISN_THROW:
+       case ISN_TRYCONT:
+       case ISN_UNLETINDEX:
+       case ISN_UNLETRANGE:
+       case ISN_UNPACK:
+       case ISN_USEDICT:
        // nothing allocated
        break;
-}
+    }
 }
 
     void