changeset 23725:f98692ae09a0 v8.2.2404

patch 8.2.2404: Vim9: profiling try/catch not correct Commit: https://github.com/vim/vim/commit/107e9cecf72928b4aaaf08b73bf2d191924ab695 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 24 20:52:00 2021 +0100 patch 8.2.2404: Vim9: profiling try/catch not correct Problem: Vim9: profiling try/catch not correct. Solution: Add profile instructions. Fix that "entry" did not rethrow an excpetion.
author Bram Moolenaar <Bram@vim.org>
date Sun, 24 Jan 2021 21:00:04 +0100
parents 85e6fa8b86f7
children d0bacfdcaa82
files src/testdir/test_profile.vim src/version.c src/vim9compile.c src/vim9execute.c
diffstat 4 files changed, 134 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_profile.vim
+++ b/src/testdir/test_profile.vim
@@ -100,11 +100,11 @@ func RunProfileFunc(command, declare, as
 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(comman
 
   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(comman
 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])
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2404,
+/**/
     2403,
 /**/
     2402,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -6566,7 +6566,7 @@ compile_jump_to_end(endlabel_T **el, jum
 }
 
     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
 	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 *cc
 
     // 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 
 
     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 
 	    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 *cct
     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 *cct
 	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);
-
-    isn->isn_arg.try.try_finally = instr->ga_len;
+    compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label,
+							     this_instr, cctx);
+
+    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;
 }
 
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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;
+		    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;
+		    }
+
+		    // 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;
 		}
-		--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;
-		}
-
-		if (throw_exception(tv->vval.v_string, ET_USER, NULL) == FAIL)
-		{
-		    vim_free(tv->vval.v_string);
-		    goto failed;
-		}
-		did_throw = TRUE;
 		break;
 
 	    // compare with special values