# HG changeset patch # User Bram Moolenaar # Date 1595079003 -7200 # Node ID 8e1081ede3b8538d9bbfb13a9869fa6d5e206525 # Parent a652dd15819ad333ca128c92b0210b2830fca5a7 patch 8.2.1236: Vim9: a few errors not caught by try/catch Commit: https://github.com/vim/vim/commit/e859312e748297bde67a053fd3c486fc2c14b532 Author: Bram Moolenaar Date: Sat Jul 18 15:17:02 2020 +0200 patch 8.2.1236: Vim9: a few errors not caught by try/catch Problem: Vim9: a few errors not caught by try/catch. Solution: Do not bail out if an error is inside try/catch. Fix that a not matching catch doesn't jump to :endtry. diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -511,6 +511,22 @@ def Test_try_catch() endtry # comment assert_equal(['1', 'wrong', '3'], l) + l = [] + try + try + add(l, '1') + throw 'wrong' + add(l, '2') + catch /right/ + add(l, v:exception) + endtry + catch /wrong/ + add(l, 'caught') + finally + add(l, 'finally') + endtry + assert_equal(['1', 'caught', 'finally'], l) + let n: number try n = l[3] @@ -591,15 +607,63 @@ def Test_try_catch() endtry assert_equal(277, n) - # TODO: make this work - # try - # &ts = g:astring - # catch /E1093:/ - # n = 288 - # endtry - # assert_equal(288, n) + try + &ts = g:astring + catch /E1029:/ + n = 288 + endtry + assert_equal(288, n) + + try + &backspace = 'asdf' + catch /E474:/ + n = 299 + endtry + assert_equal(299, n) + + l = [1] + try + l[3] = 3 + catch /E684:/ + n = 300 + endtry + assert_equal(300, n) + + try + d[''] = 3 + catch /E713:/ + n = 311 + endtry + assert_equal(311, n) + + try + unlet g:does_not_exist + catch /E108:/ + n = 322 + endtry + assert_equal(322, n) + + try + d = {'text': 1, g:astring: 2} + catch /E721:/ + n = 333 + endtry + assert_equal(333, n) + + try + l = DeletedFunc() + catch /E933:/ + n = 344 + endtry + assert_equal(344, n) enddef +def DeletedFunc(): list + return ['delete me'] +enddef +defcompile +delfunc DeletedFunc + def ThrowFromDef() throw "getout" # comment enddef diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1236, +/**/ 1235, /**/ 1234, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4961,6 +4961,7 @@ compile_assignment(char_u *arg, exarg_T } if (need_type(stacktype, &t_list_any, -1, cctx, FALSE) == FAIL) goto theend; + // TODO: check the length of a constant list here generate_CHECKLEN(cctx, semicolon ? var_count - 1 : var_count, semicolon); } @@ -6539,6 +6540,7 @@ compile_finally(char_u *arg, cctx_T *cct // 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; + scope->se_u.se_try.ts_catch_label = 0; } // TODO: set index in ts_finally_label jumps @@ -6584,8 +6586,18 @@ compile_endtry(char_u *arg, cctx_T *cctx compile_fill_jump_to_end(&scope->se_u.se_try.ts_end_label, cctx); // End :catch or :finally scope: set value in ISN_TRY instruction + if (isn->isn_arg.try.try_catch == 0) + isn->isn_arg.try.try_catch = instr->ga_len; if (isn->isn_arg.try.try_finally == 0) isn->isn_arg.try.try_finally = instr->ga_len; + + if (scope->se_u.se_try.ts_catch_label != 0) + { + // Last 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; + } + compile_endblock(cctx); if (generate_instr(cctx, ISN_ENDTRY) == NULL) diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1241,7 +1241,7 @@ call_def_function( if (msg != NULL) { emsg(_(msg)); - goto failed; + goto on_error; } } break; @@ -1272,7 +1272,8 @@ call_def_function( --ectx.ec_stack.ga_len; if (set_vim_var_tv(iptr->isn_arg.number, STACK_TV_BOT(0)) == FAIL) - goto failed; + // should not happen, type is checked when compiling + goto on_error; break; // store g:/b:/w:/t: variable @@ -1335,7 +1336,7 @@ call_def_function( if (lidx < 0 || lidx > list->lv_len) { semsg(_(e_listidx), lidx); - goto failed; + goto on_error; } tv = STACK_TV_BOT(-3); if (lidx < list->lv_len) @@ -1348,7 +1349,7 @@ call_def_function( } else { - // append to list + // append to list, only fails when out of memory if (list_append_tv(list, tv) == FAIL) goto failed; clear_tv(tv); @@ -1371,18 +1372,19 @@ call_def_function( if (key == NULL || *key == NUL) { emsg(_(e_emptykey)); - goto failed; + goto on_error; } tv = STACK_TV_BOT(-3); di = dict_find(dict, key, -1); if (di != NULL) { + // overwrite existing value clear_tv(&di->di_tv); di->di_tv = *tv; } else { - // add to dict + // add to dict, only fails when out of memory if (dict_add_tv(dict, (char *)key, tv) == FAIL) goto failed; clear_tv(tv); @@ -1465,7 +1467,7 @@ call_def_function( case ISN_UNLET: if (do_unlet(iptr->isn_arg.unlet.ul_name, iptr->isn_arg.unlet.ul_forceit) == FAIL) - goto failed; + goto on_error; break; case ISN_UNLETENV: vim_unsetenv(iptr->isn_arg.unlet.ul_name); @@ -1481,24 +1483,38 @@ call_def_function( // create a dict from items on the stack case ISN_NEWDICT: { - int count = iptr->isn_arg.number; - dict_T *dict = dict_alloc(); - dictitem_T *item; + int count = iptr->isn_arg.number; + dict_T *dict = dict_alloc(); + dictitem_T *item; if (dict == NULL) goto failed; for (idx = 0; idx < count; ++idx) { - // check key type is VAR_STRING + // have already checked key type is VAR_STRING tv = STACK_TV_BOT(2 * (idx - count)); + // check key is unique + item = dict_find(dict, tv->vval.v_string, -1); + if (item != NULL) + { + semsg(_(e_duplicate_key), tv->vval.v_string); + dict_unref(dict); + goto on_error; + } item = dictitem_alloc(tv->vval.v_string); clear_tv(tv); if (item == NULL) + { + dict_unref(dict); goto failed; + } item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1); item->di_tv.v_lock = 0; if (dict_add(dict, item) == FAIL) + { + dict_unref(dict); goto failed; + } } if (count > 0) @@ -1519,7 +1535,7 @@ call_def_function( if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, iptr->isn_arg.dfunc.cdf_argcount, &ectx) == FAIL) - goto failed; + goto on_error; break; // call a builtin function