]> granicus.if.org Git - vim/commitdiff
patch 8.2.2404: Vim9: profiling try/catch not correct v8.2.2404
authorBram Moolenaar <Bram@vim.org>
Sun, 24 Jan 2021 19:52:00 +0000 (20:52 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 24 Jan 2021 19:52:00 +0000 (20:52 +0100)
Problem:    Vim9: profiling try/catch not correct.
Solution:   Add profile instructions.  Fix that "entry" did not rethrow an
            excpetion.

src/testdir/test_profile.vim
src/version.c
src/vim9compile.c
src/vim9execute.c

index d037c778e599a5361cb963bbb850baa95e169d42..8aaac90e890cb596c631b03ad866448c4ec0207d 100644 (file)
@@ -100,11 +100,11 @@ func RunProfileFunc(command, declare, assign)
 endfunc
 
 func Test_profile_func_with_ifelse()
-  call Run_profile_func_with_ifelse('func', 'let', 'let')
-  call Run_profile_func_with_ifelse('def', 'var', '')
+  call Run_profile_func_with_ifelse('func', 'let')
+  call Run_profile_func_with_ifelse('def', 'var')
 endfunc
 
-func Run_profile_func_with_ifelse(command, declare, assign)
+func Run_profile_func_with_ifelse(command, declare)
   let lines =<< trim [CODE]
     XXX Foo1()
       if 1
@@ -140,7 +140,6 @@ func Run_profile_func_with_ifelse(command, declare, assign)
 
   call map(lines, {k, v -> substitute(v, 'XXX', a:command, '') })
   call map(lines, {k, v -> substitute(v, 'DDD', a:declare, '') })
-  call map(lines, {k, v -> substitute(v, 'AAA', a:assign, '') })
 
   call writefile(lines, 'Xprofile_func.vim')
   call system(GetVimCommand()
@@ -219,42 +218,56 @@ func Run_profile_func_with_ifelse(command, declare, assign)
 endfunc
 
 func Test_profile_func_with_trycatch()
+  call Run_profile_func_with_trycatch('func', 'let')
+  call Run_profile_func_with_trycatch('def', 'var')
+endfunc
+
+func Run_profile_func_with_trycatch(command, declare)
   let lines =<< trim [CODE]
-    func! Foo1()
+    XXX Foo1()
       try
-        let x = 0
+        DDD x = 0
       catch
-        let x = 1
+        DDD x = 1
       finally
-        let x = 2
+        DDD x = 2
       endtry
-    endfunc
-    func! Foo2()
+    endXXX
+    XXX Foo2()
       try
         throw 0
       catch
-        let x = 1
+        DDD x = 1
       finally
-        let x = 2
+        DDD x = 2
       endtry
-    endfunc
-    func! Foo3()
+    endXXX
+    XXX Foo3()
       try
         throw 0
       catch
         throw 1
       finally
-        let x = 2
+        DDD x = 2
       endtry
-    endfunc
+    endXXX
     call Foo1()
     call Foo2()
+    let rethrown = 0
     try
       call Foo3()
     catch
+      let rethrown = 1
     endtry
+    if rethrown != 1
+      " call Foo1 again so that the test fails
+      call Foo1()
+    endif
   [CODE]
 
+  call map(lines, {k, v -> substitute(v, 'XXX', a:command, '') })
+  call map(lines, {k, v -> substitute(v, 'DDD', a:declare, '') })
+
   call writefile(lines, 'Xprofile_func.vim')
   call system(GetVimCommand()
     \ . ' -es -i NONE --noplugin'
@@ -279,11 +292,11 @@ func Test_profile_func_with_trycatch()
   call assert_equal('',                                            lines[5])
   call assert_equal('count  total (s)   self (s)',                 lines[6])
   call assert_match('^\s*1\s\+.*\stry$',                           lines[7])
-  call assert_match('^\s*1\s\+.*\s  let x = 0$',                   lines[8])
+  call assert_match('^\s*1\s\+.*\s  \(let\|var\) x = 0$',          lines[8])
   call assert_match(        '^\s\+catch$',                         lines[9])
-  call assert_match(          '^\s\+let x = 1$',                   lines[10])
+  call assert_match(          '^\s\+\(let\|var\) x = 1$',          lines[10])
   call assert_match('^\s*1\s\+.*\sfinally$',                       lines[11])
-  call assert_match('^\s*1\s\+.*\s  let x = 2$',                   lines[12])
+  call assert_match('^\s*1\s\+.*\s  \(let\|var\) x = 2$',          lines[12])
   call assert_match('^\s*1\s\+.*\sendtry$',                        lines[13])
   call assert_equal('',                                            lines[14])
   call assert_equal('FUNCTION  Foo2()',                            lines[15])
@@ -295,9 +308,9 @@ func Test_profile_func_with_trycatch()
   call assert_match('^\s*1\s\+.*\stry$',                           lines[22])
   call assert_match('^\s*1\s\+.*\s  throw 0$',                     lines[23])
   call assert_match('^\s*1\s\+.*\scatch$',                         lines[24])
-  call assert_match('^\s*1\s\+.*\s  let x = 1$',                   lines[25])
+  call assert_match('^\s*1\s\+.*\s  \(let\|var\) x = 1$',          lines[25])
   call assert_match('^\s*1\s\+.*\sfinally$',                       lines[26])
-  call assert_match('^\s*1\s\+.*\s  let x = 2$',                   lines[27])
+  call assert_match('^\s*1\s\+.*\s  \(let\|var\) x = 2$',          lines[27])
   call assert_match('^\s*1\s\+.*\sendtry$',                        lines[28])
   call assert_equal('',                                            lines[29])
   call assert_equal('FUNCTION  Foo3()',                            lines[30])
@@ -311,7 +324,7 @@ func Test_profile_func_with_trycatch()
   call assert_match('^\s*1\s\+.*\scatch$',                         lines[39])
   call assert_match('^\s*1\s\+.*\s  throw 1$',                     lines[40])
   call assert_match('^\s*1\s\+.*\sfinally$',                       lines[41])
-  call assert_match('^\s*1\s\+.*\s  let x = 2$',                   lines[42])
+  call assert_match('^\s*1\s\+.*\s  \(let\|var\) x = 2$',          lines[42])
   call assert_match(        '^\s\+endtry$',                        lines[43])
   call assert_equal('',                                            lines[44])
   call assert_equal('FUNCTIONS SORTED ON TOTAL TIME',              lines[45])
index 554231fd006cbe099231cae219d78f9dc1e7abaf..43ad049da51fc1595dbc71a812c9df23cc2dfae9 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2404,
 /**/
     2403,
 /**/
index 995083099c9af8bf48537e2c008c92bcb86e54a0..9497782ff94c3505b36e507d4dc95d94288125f2 100644 (file)
@@ -6566,7 +6566,7 @@ compile_jump_to_end(endlabel_T **el, jumpwhen_T when, cctx_T *cctx)
 }
 
     static void
-compile_fill_jump_to_end(endlabel_T **el, cctx_T *cctx)
+compile_fill_jump_to_end(endlabel_T **el, int jump_where, cctx_T *cctx)
 {
     garray_T   *instr = &cctx->ctx_instr;
 
@@ -6576,7 +6576,7 @@ compile_fill_jump_to_end(endlabel_T **el, cctx_T *cctx)
        isn_T       *isn;
 
        isn = ((isn_T *)instr->ga_data) + cur->el_end_label;
-       isn->isn_arg.jump.jump_where = instr->ga_len;
+       isn->isn_arg.jump.jump_where = jump_where;
        *el = cur->el_next;
        vim_free(cur);
     }
@@ -6939,7 +6939,7 @@ compile_endif(char_u *arg, cctx_T *cctx)
        isn->isn_arg.jump.jump_where = instr->ga_len;
     }
     // Fill in the "end" label in jumps at the end of the blocks.
-    compile_fill_jump_to_end(&ifscope->is_end_label, cctx);
+    compile_fill_jump_to_end(&ifscope->is_end_label, instr->ga_len, cctx);
 
 #ifdef FEAT_PROFILE
     // even when skipping we count the endif as executed, unless the block it's
@@ -7182,7 +7182,7 @@ compile_endfor(char_u *arg, cctx_T *cctx)
     isn->isn_arg.forloop.for_end = instr->ga_len;
 
     // Fill in the "end" label any BREAK statements
-    compile_fill_jump_to_end(&forscope->fs_end_label, cctx);
+    compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx);
 
     // Below the ":for" scope drop the "expr" list from the stack.
     if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
@@ -7245,6 +7245,7 @@ compile_while(char_u *arg, cctx_T *cctx)
 compile_endwhile(char_u *arg, cctx_T *cctx)
 {
     scope_T    *scope = cctx->ctx_scope;
+    garray_T   *instr = &cctx->ctx_instr;
 
     if (scope == NULL || scope->se_type != WHILE_SCOPE)
     {
@@ -7264,7 +7265,8 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
 
     // Fill in the "end" label in the WHILE statement so it can jump here.
     // And in any jumps for ":break"
-    compile_fill_jump_to_end(&scope->se_u.se_while.ws_end_label, cctx);
+    compile_fill_jump_to_end(&scope->se_u.se_while.ws_end_label,
+                                                         instr->ga_len, cctx);
 
     vim_free(scope);
 
@@ -7446,6 +7448,12 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED)
 
     if (cctx->ctx_skip != SKIP_YES)
     {
+#ifdef FEAT_PROFILE
+       // the profile-start should be after the jump
+       if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
+                                                  .isn_type == ISN_PROF_START)
+           --instr->ga_len;
+#endif
        // Jump from end of previous block to :finally or :endtry
        if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
                                                    JUMP_ALWAYS, cctx) == FAIL)
@@ -7461,6 +7469,15 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED)
            isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label;
            isn->isn_arg.jump.jump_where = instr->ga_len;
        }
+#ifdef FEAT_PROFILE
+       if (cctx->ctx_profiling)
+       {
+           // a "throw" that jumps here needs to be counted
+           generate_instr(cctx, ISN_PROF_END);
+           // the "catch" is also counted
+           generate_instr(cctx, ISN_PROF_START);
+       }
+#endif
     }
 
     p = skipwhite(arg);
@@ -7521,6 +7538,7 @@ compile_finally(char_u *arg, cctx_T *cctx)
     scope_T    *scope = cctx->ctx_scope;
     garray_T   *instr = &cctx->ctx_instr;
     isn_T      *isn;
+    int                this_instr;
 
     // end block scope from :try or :catch
     if (scope != NULL && scope->se_type == BLOCK_SCOPE)
@@ -7542,15 +7560,24 @@ compile_finally(char_u *arg, cctx_T *cctx)
        return NULL;
     }
 
+    this_instr = instr->ga_len;
+#ifdef FEAT_PROFILE
+    if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
+                                                  .isn_type == ISN_PROF_START)
+       // jump to the profile start of the "finally"
+       --this_instr;
+#endif
+
     // Fill in the "end" label in jumps at the end of the blocks.
-    compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label, cctx);
+    compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label,
+                                                            this_instr, cctx);
 
-    isn->isn_arg.try.try_finally = instr->ga_len;
+    isn->isn_arg.try.try_finally = this_instr;
     if (scope->se_u.se_try.ts_catch_label != 0)
     {
        // Previous catch without match jumps here
        isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label;
-       isn->isn_arg.jump.jump_where = instr->ga_len;
+       isn->isn_arg.jump.jump_where = this_instr;
        scope->se_u.se_try.ts_catch_label = 0;
     }
 
@@ -7595,9 +7622,18 @@ compile_endtry(char_u *arg, cctx_T *cctx)
            return NULL;
        }
 
+#ifdef FEAT_PROFILE
+    if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
+                                                  .isn_type == ISN_PROF_START)
+       // move the profile start after "endtry" so that it's not counted when
+       // the exception is rethrown.
+       --instr->ga_len;
+#endif
+
        // Fill in the "end" label in jumps at the end of the blocks, if not
        // done by ":finally".
-       compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label, cctx);
+       compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label,
+                                                         instr->ga_len, cctx);
 
        // End :catch or :finally scope: set value in ISN_TRY instruction
        if (isn->isn_arg.try.try_catch == 0)
@@ -7617,6 +7653,10 @@ compile_endtry(char_u *arg, cctx_T *cctx)
 
     if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_ENDTRY) == NULL)
        return NULL;
+#ifdef FEAT_PROFILE
+       if (cctx->ctx_profiling)
+           generate_instr(cctx, ISN_PROF_START);
+#endif
     return arg;
 }
 
index f640684c6eb7d75a031bee70de4dfeb6d542298a..4c144f67aee3024328887c50c96a05a62ce08b28 100644 (file)
@@ -2613,7 +2613,7 @@ call_def_function(
 
                    if (trystack->ga_len > 0)
                    {
-                       trycmd_T    *trycmd = NULL;
+                       trycmd_T    *trycmd;
 
                        --trystack->ga_len;
                        --trylevel;
@@ -2635,34 +2635,54 @@ call_def_function(
                break;
 
            case ISN_THROW:
-               if (ectx.ec_trystack.ga_len == 0 && trylevel == 0
-                                                               && emsg_silent)
                {
-                   // throwing an exception while using "silent!" causes the
-                   // function to abort but not display an error.
-                   tv = STACK_TV_BOT(-1);
-                   clear_tv(tv);
-                   tv->v_type = VAR_NUMBER;
-                   tv->vval.v_number = 0;
-                   goto done;
-               }
-               --ectx.ec_stack.ga_len;
-               tv = STACK_TV_BOT(0);
-               if (tv->vval.v_string == NULL
+                   garray_T    *trystack = &ectx.ec_trystack;
+
+                   if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent)
+                   {
+                       // throwing an exception while using "silent!" causes
+                       // the function to abort but not display an error.
+                       tv = STACK_TV_BOT(-1);
+                       clear_tv(tv);
+                       tv->v_type = VAR_NUMBER;
+                       tv->vval.v_number = 0;
+                       goto done;
+                   }
+                   --ectx.ec_stack.ga_len;
+                   tv = STACK_TV_BOT(0);
+                   if (tv->vval.v_string == NULL
                                       || *skipwhite(tv->vval.v_string) == NUL)
-               {
-                   vim_free(tv->vval.v_string);
-                   SOURCING_LNUM = iptr->isn_lnum;
-                   emsg(_(e_throw_with_empty_string));
-                   goto failed;
-               }
+                   {
+                       vim_free(tv->vval.v_string);
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       emsg(_(e_throw_with_empty_string));
+                       goto failed;
+                   }
 
-               if (throw_exception(tv->vval.v_string, ET_USER, NULL) == FAIL)
-               {
-                   vim_free(tv->vval.v_string);
-                   goto failed;
+                   // Inside a "catch" we need to first discard the caught
+                   // exception.
+                   if (trystack->ga_len > 0)
+                   {
+                       trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
+                                                       + trystack->ga_len - 1;
+                       if (trycmd->tcd_caught && current_exception != NULL)
+                       {
+                           // discard the exception
+                           if (caught_stack == current_exception)
+                               caught_stack = caught_stack->caught;
+                           discard_current_exception();
+                           trycmd->tcd_caught = FALSE;
+                       }
+                   }
+
+                   if (throw_exception(tv->vval.v_string, ET_USER, NULL)
+                                                                      == FAIL)
+                   {
+                       vim_free(tv->vval.v_string);
+                       goto failed;
+                   }
+                   did_throw = TRUE;
                }
-               did_throw = TRUE;
                break;
 
            // compare with special values