mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
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.
This commit is contained in:
@@ -100,11 +100,11 @@ func RunProfileFunc(command, declare, assign)
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_profile_func_with_ifelse()
|
func Test_profile_func_with_ifelse()
|
||||||
call Run_profile_func_with_ifelse('func', 'let', 'let')
|
call Run_profile_func_with_ifelse('func', 'let')
|
||||||
call Run_profile_func_with_ifelse('def', 'var', '')
|
call Run_profile_func_with_ifelse('def', 'var')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Run_profile_func_with_ifelse(command, declare, assign)
|
func Run_profile_func_with_ifelse(command, declare)
|
||||||
let lines =<< trim [CODE]
|
let lines =<< trim [CODE]
|
||||||
XXX Foo1()
|
XXX Foo1()
|
||||||
if 1
|
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, 'XXX', a:command, '') })
|
||||||
call map(lines, {k, v -> substitute(v, 'DDD', a:declare, '') })
|
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 writefile(lines, 'Xprofile_func.vim')
|
||||||
call system(GetVimCommand()
|
call system(GetVimCommand()
|
||||||
@@ -219,42 +218,56 @@ func Run_profile_func_with_ifelse(command, declare, assign)
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_profile_func_with_trycatch()
|
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]
|
let lines =<< trim [CODE]
|
||||||
func! Foo1()
|
XXX Foo1()
|
||||||
try
|
try
|
||||||
let x = 0
|
DDD x = 0
|
||||||
catch
|
catch
|
||||||
let x = 1
|
DDD x = 1
|
||||||
finally
|
finally
|
||||||
let x = 2
|
DDD x = 2
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endXXX
|
||||||
func! Foo2()
|
XXX Foo2()
|
||||||
try
|
try
|
||||||
throw 0
|
throw 0
|
||||||
catch
|
catch
|
||||||
let x = 1
|
DDD x = 1
|
||||||
finally
|
finally
|
||||||
let x = 2
|
DDD x = 2
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endXXX
|
||||||
func! Foo3()
|
XXX Foo3()
|
||||||
try
|
try
|
||||||
throw 0
|
throw 0
|
||||||
catch
|
catch
|
||||||
throw 1
|
throw 1
|
||||||
finally
|
finally
|
||||||
let x = 2
|
DDD x = 2
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endXXX
|
||||||
call Foo1()
|
call Foo1()
|
||||||
call Foo2()
|
call Foo2()
|
||||||
|
let rethrown = 0
|
||||||
try
|
try
|
||||||
call Foo3()
|
call Foo3()
|
||||||
catch
|
catch
|
||||||
|
let rethrown = 1
|
||||||
endtry
|
endtry
|
||||||
|
if rethrown != 1
|
||||||
|
" call Foo1 again so that the test fails
|
||||||
|
call Foo1()
|
||||||
|
endif
|
||||||
[CODE]
|
[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 writefile(lines, 'Xprofile_func.vim')
|
||||||
call system(GetVimCommand()
|
call system(GetVimCommand()
|
||||||
\ . ' -es -i NONE --noplugin'
|
\ . ' -es -i NONE --noplugin'
|
||||||
@@ -279,11 +292,11 @@ func Test_profile_func_with_trycatch()
|
|||||||
call assert_equal('', lines[5])
|
call assert_equal('', lines[5])
|
||||||
call assert_equal('count total (s) self (s)', lines[6])
|
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\+.*\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\+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\+.*\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_match('^\s*1\s\+.*\sendtry$', lines[13])
|
||||||
call assert_equal('', lines[14])
|
call assert_equal('', lines[14])
|
||||||
call assert_equal('FUNCTION Foo2()', lines[15])
|
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\+.*\stry$', lines[22])
|
||||||
call assert_match('^\s*1\s\+.*\s throw 0$', lines[23])
|
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\+.*\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\+.*\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_match('^\s*1\s\+.*\sendtry$', lines[28])
|
||||||
call assert_equal('', lines[29])
|
call assert_equal('', lines[29])
|
||||||
call assert_equal('FUNCTION Foo3()', lines[30])
|
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\+.*\scatch$', lines[39])
|
||||||
call assert_match('^\s*1\s\+.*\s throw 1$', lines[40])
|
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\+.*\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_match( '^\s\+endtry$', lines[43])
|
||||||
call assert_equal('', lines[44])
|
call assert_equal('', lines[44])
|
||||||
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[45])
|
call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[45])
|
||||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2404,
|
||||||
/**/
|
/**/
|
||||||
2403,
|
2403,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -6566,7 +6566,7 @@ compile_jump_to_end(endlabel_T **el, jumpwhen_T when, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
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_T *isn;
|
||||||
|
|
||||||
isn = ((isn_T *)instr->ga_data) + cur->el_end_label;
|
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;
|
*el = cur->el_next;
|
||||||
vim_free(cur);
|
vim_free(cur);
|
||||||
}
|
}
|
||||||
@@ -6939,7 +6939,7 @@ compile_endif(char_u *arg, cctx_T *cctx)
|
|||||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||||
}
|
}
|
||||||
// Fill in the "end" label in jumps at the end of the blocks.
|
// 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
|
#ifdef FEAT_PROFILE
|
||||||
// even when skipping we count the endif as executed, unless the block it's
|
// 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;
|
isn->isn_arg.forloop.for_end = instr->ga_len;
|
||||||
|
|
||||||
// Fill in the "end" label any BREAK statements
|
// 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.
|
// Below the ":for" scope drop the "expr" list from the stack.
|
||||||
if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
|
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)
|
compile_endwhile(char_u *arg, cctx_T *cctx)
|
||||||
{
|
{
|
||||||
scope_T *scope = cctx->ctx_scope;
|
scope_T *scope = cctx->ctx_scope;
|
||||||
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
|
|
||||||
if (scope == NULL || scope->se_type != WHILE_SCOPE)
|
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.
|
// Fill in the "end" label in the WHILE statement so it can jump here.
|
||||||
// And in any jumps for ":break"
|
// 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);
|
vim_free(scope);
|
||||||
|
|
||||||
@@ -7446,6 +7448,12 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED)
|
|||||||
|
|
||||||
if (cctx->ctx_skip != SKIP_YES)
|
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
|
// Jump from end of previous block to :finally or :endtry
|
||||||
if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
|
if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
|
||||||
JUMP_ALWAYS, cctx) == FAIL)
|
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_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 = 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);
|
p = skipwhite(arg);
|
||||||
@@ -7521,6 +7538,7 @@ compile_finally(char_u *arg, cctx_T *cctx)
|
|||||||
scope_T *scope = cctx->ctx_scope;
|
scope_T *scope = cctx->ctx_scope;
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
|
int this_instr;
|
||||||
|
|
||||||
// end block scope from :try or :catch
|
// end block scope from :try or :catch
|
||||||
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
|
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
|
||||||
@@ -7542,15 +7560,24 @@ compile_finally(char_u *arg, cctx_T *cctx)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill in the "end" label in jumps at the end of the blocks.
|
this_instr = instr->ga_len;
|
||||||
compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label, cctx);
|
#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
|
||||||
|
|
||||||
isn->isn_arg.try.try_finally = instr->ga_len;
|
// 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,
|
||||||
|
this_instr, cctx);
|
||||||
|
|
||||||
|
isn->isn_arg.try.try_finally = this_instr;
|
||||||
if (scope->se_u.se_try.ts_catch_label != 0)
|
if (scope->se_u.se_try.ts_catch_label != 0)
|
||||||
{
|
{
|
||||||
// Previous catch without match jumps here
|
// Previous catch without match jumps here
|
||||||
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_try.ts_catch_label;
|
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;
|
scope->se_u.se_try.ts_catch_label = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7595,9 +7622,18 @@ compile_endtry(char_u *arg, cctx_T *cctx)
|
|||||||
return NULL;
|
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
|
// Fill in the "end" label in jumps at the end of the blocks, if not
|
||||||
// done by ":finally".
|
// 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
|
// End :catch or :finally scope: set value in ISN_TRY instruction
|
||||||
if (isn->isn_arg.try.try_catch == 0)
|
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)
|
if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_ENDTRY) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
#ifdef FEAT_PROFILE
|
||||||
|
if (cctx->ctx_profiling)
|
||||||
|
generate_instr(cctx, ISN_PROF_START);
|
||||||
|
#endif
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2613,7 +2613,7 @@ call_def_function(
|
|||||||
|
|
||||||
if (trystack->ga_len > 0)
|
if (trystack->ga_len > 0)
|
||||||
{
|
{
|
||||||
trycmd_T *trycmd = NULL;
|
trycmd_T *trycmd;
|
||||||
|
|
||||||
--trystack->ga_len;
|
--trystack->ga_len;
|
||||||
--trylevel;
|
--trylevel;
|
||||||
@@ -2635,34 +2635,54 @@ call_def_function(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ISN_THROW:
|
case ISN_THROW:
|
||||||
if (ectx.ec_trystack.ga_len == 0 && trylevel == 0
|
|
||||||
&& emsg_silent)
|
|
||||||
{
|
{
|
||||||
// throwing an exception while using "silent!" causes the
|
garray_T *trystack = &ectx.ec_trystack;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (throw_exception(tv->vval.v_string, ET_USER, NULL) == FAIL)
|
if (trystack->ga_len == 0 && trylevel == 0 && emsg_silent)
|
||||||
{
|
{
|
||||||
vim_free(tv->vval.v_string);
|
// throwing an exception while using "silent!" causes
|
||||||
goto failed;
|
// 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;
|
||||||
}
|
}
|
||||||
did_throw = TRUE;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// compare with special values
|
// compare with special values
|
||||||
|
Reference in New Issue
Block a user