Mercurial > vim
changeset 23679:e8c379b20765 v8.2.2381
patch 8.2.2381: Vim9: divide by zero does not abort expression execution
Commit: https://github.com/vim/vim/commit/c5f59fab23820454f060562927ddc1397f9d479a
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Jan 21 12:34:14 2021 +0100
patch 8.2.2381: Vim9: divide by zero does not abort expression execution
Problem: Vim9: divide by zero does not abort expression execution.
Solution: Use a "failed" flag. (issue https://github.com/vim/vim/issues/7704)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Thu, 21 Jan 2021 12:45:05 +0100 |
parents | 8548a27908be |
children | edc03877acad |
files | src/eval.c src/evalvars.c src/proto/eval.pro src/testdir/test_vim9_assign.vim src/testdir/vim9.vim src/version.c src/vim9compile.c |
diffstat | 7 files changed, 77 insertions(+), 16 deletions(-) [+] |
line wrap: on
line diff
--- a/src/eval.c +++ b/src/eval.c @@ -57,16 +57,21 @@ static char_u *make_expanded_name(char_u /* * Return "n1" divided by "n2", taking care of dividing by zero. + * If "failed" is not NULL set it to TRUE when dividing by zero fails. */ varnumber_T -num_divide(varnumber_T n1, varnumber_T n2) +num_divide(varnumber_T n1, varnumber_T n2, int *failed) { varnumber_T result; if (n2 == 0) { if (in_vim9script()) + { emsg(_(e_divide_by_zero)); + if (failed != NULL) + *failed = TRUE; + } if (n1 == 0) result = VARNUM_MIN; // similar to NaN else if (n1 < 0) @@ -82,12 +87,17 @@ num_divide(varnumber_T n1, varnumber_T n /* * Return "n1" modulus "n2", taking care of dividing by zero. + * If "failed" is not NULL set it to TRUE when dividing by zero fails. */ varnumber_T -num_modulus(varnumber_T n1, varnumber_T n2) +num_modulus(varnumber_T n1, varnumber_T n2, int *failed) { if (n2 == 0 && in_vim9script()) + { emsg(_(e_divide_by_zero)); + if (failed != NULL) + *failed = TRUE; + } return (n2 == 0) ? 0 : (n1 % n2); } @@ -1516,6 +1526,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char varnumber_T n; char_u numbuf[NUMBUFLEN]; char_u *s; + int failed = FALSE; // Can't do anything with a Funcref, Dict, v:true on the right. if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT @@ -1599,8 +1610,10 @@ tv_op(typval_T *tv1, typval_T *tv2, char case '+': n += tv_get_number(tv2); break; case '-': n -= tv_get_number(tv2); break; case '*': n *= tv_get_number(tv2); break; - case '/': n = num_divide(n, tv_get_number(tv2)); break; - case '%': n = num_modulus(n, tv_get_number(tv2)); break; + case '/': n = num_divide(n, tv_get_number(tv2), + &failed); break; + case '%': n = num_modulus(n, tv_get_number(tv2), + &failed); break; } clear_tv(tv1); tv1->v_type = VAR_NUMBER; @@ -1619,7 +1632,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char tv1->v_type = VAR_STRING; tv1->vval.v_string = s; } - return OK; + return failed ? FAIL : OK; case VAR_FLOAT: #ifdef FEAT_FLOAT @@ -3196,12 +3209,16 @@ eval6( else #endif { + int failed = FALSE; + if (op == '*') n1 = n1 * n2; else if (op == '/') - n1 = num_divide(n1, n2); + n1 = num_divide(n1, n2, &failed); else - n1 = num_modulus(n1, n2); + n1 = num_modulus(n1, n2, &failed); + if (failed) + return FAIL; rettv->v_type = VAR_NUMBER; rettv->vval.v_number = n1;
--- a/src/evalvars.c +++ b/src/evalvars.c @@ -1410,8 +1410,10 @@ ex_let_one( case '+': n = numval + n; break; case '-': n = numval - n; break; case '*': n = numval * n; break; - case '/': n = (long)num_divide(numval, n); break; - case '%': n = (long)num_modulus(numval, n); break; + case '/': n = (long)num_divide(numval, n, + &failed); break; + case '%': n = (long)num_modulus(numval, n, + &failed); break; } } else if (opt_type == gov_string
--- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -1,6 +1,6 @@ /* eval.c */ -varnumber_T num_divide(varnumber_T n1, varnumber_T n2); -varnumber_T num_modulus(varnumber_T n1, varnumber_T n2); +varnumber_T num_divide(varnumber_T n1, varnumber_T n2, int *failed); +varnumber_T num_modulus(varnumber_T n1, varnumber_T n2, int *failed); void eval_init(void); void eval_clear(void); void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip);
--- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -1489,6 +1489,30 @@ def Test_unlet() assert_equal('', $ENVVAR) enddef +def Test_expr_error_no_assign() + var lines =<< trim END + vim9script + var x = invalid + echo x + END + CheckScriptFailureList(lines, ['E121:', 'E121:']) + + lines =<< trim END + vim9script + var x = 1 / 0 + echo x + END + CheckScriptFailureList(lines, ['E1154:', 'E121:']) + + lines =<< trim END + vim9script + var x = 1 % 0 + echo x + END + CheckScriptFailureList(lines, ['E1154:', 'E121:']) +enddef + + def Test_assign_command_modifier() var lines =<< trim END var verbose = 0
--- a/src/testdir/vim9.vim +++ b/src/testdir/vim9.vim @@ -69,6 +69,19 @@ def CheckScriptFailure(lines: list<strin endtry enddef +def CheckScriptFailureList(lines: list<string>, errors: list<string>, lnum = -3) + var cwd = getcwd() + var fname = 'XScriptFailure' .. s:sequence + s:sequence += 1 + writefile(lines, fname) + try + assert_fails('so ' .. fname, errors, lines, lnum) + finally + chdir(cwd) + delete(fname) + endtry +enddef + def CheckScriptSuccess(lines: list<string>) var cwd = getcwd() var fname = 'XScriptSuccess' .. s:sequence
--- 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 */ /**/ + 2381, +/**/ 2380, /**/ 2379,
--- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4291,9 +4291,10 @@ compile_expr6(char_u **arg, cctx_T *cctx && ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER && ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER) { - typval_T *tv1 = &ppconst->pp_tv[ppconst_used]; - typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1]; - varnumber_T res = 0; + typval_T *tv1 = &ppconst->pp_tv[ppconst_used]; + typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1]; + varnumber_T res = 0; + int failed = FALSE; // both are numbers: compute the result switch (*op) @@ -4301,12 +4302,14 @@ compile_expr6(char_u **arg, cctx_T *cctx case '*': res = tv1->vval.v_number * tv2->vval.v_number; break; case '/': res = num_divide(tv1->vval.v_number, - tv2->vval.v_number); + tv2->vval.v_number, &failed); break; case '%': res = num_modulus(tv1->vval.v_number, - tv2->vval.v_number); + tv2->vval.v_number, &failed); break; } + if (failed) + return FAIL; tv1->vval.v_number = res; --ppconst->pp_used; }