0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

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 #7704)
This commit is contained in:
Bram Moolenaar
2021-01-21 12:34:14 +01:00
parent a0f7f73ebb
commit c5f59fab23
7 changed files with 77 additions and 16 deletions

View File

@@ -57,16 +57,21 @@ static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *
/* /*
* Return "n1" divided by "n2", taking care of dividing by zero. * 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 varnumber_T
num_divide(varnumber_T n1, varnumber_T n2) num_divide(varnumber_T n1, varnumber_T n2, int *failed)
{ {
varnumber_T result; varnumber_T result;
if (n2 == 0) if (n2 == 0)
{ {
if (in_vim9script()) if (in_vim9script())
{
emsg(_(e_divide_by_zero)); emsg(_(e_divide_by_zero));
if (failed != NULL)
*failed = TRUE;
}
if (n1 == 0) if (n1 == 0)
result = VARNUM_MIN; // similar to NaN result = VARNUM_MIN; // similar to NaN
else if (n1 < 0) else if (n1 < 0)
@@ -82,12 +87,17 @@ num_divide(varnumber_T n1, varnumber_T n2)
/* /*
* Return "n1" modulus "n2", taking care of dividing by zero. * 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 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()) if (n2 == 0 && in_vim9script())
{
emsg(_(e_divide_by_zero)); emsg(_(e_divide_by_zero));
if (failed != NULL)
*failed = TRUE;
}
return (n2 == 0) ? 0 : (n1 % n2); return (n2 == 0) ? 0 : (n1 % n2);
} }
@@ -1516,6 +1526,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
varnumber_T n; varnumber_T n;
char_u numbuf[NUMBUFLEN]; char_u numbuf[NUMBUFLEN];
char_u *s; char_u *s;
int failed = FALSE;
// Can't do anything with a Funcref, Dict, v:true on the right. // Can't do anything with a Funcref, Dict, v:true on the right.
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
@@ -1599,8 +1610,10 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
case '+': n += tv_get_number(tv2); break; case '+': n += tv_get_number(tv2); break;
case '-': n -= tv_get_number(tv2); break; 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_divide(n, tv_get_number(tv2),
case '%': n = num_modulus(n, tv_get_number(tv2)); break; &failed); break;
case '%': n = num_modulus(n, tv_get_number(tv2),
&failed); break;
} }
clear_tv(tv1); clear_tv(tv1);
tv1->v_type = VAR_NUMBER; tv1->v_type = VAR_NUMBER;
@@ -1619,7 +1632,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
tv1->v_type = VAR_STRING; tv1->v_type = VAR_STRING;
tv1->vval.v_string = s; tv1->vval.v_string = s;
} }
return OK; return failed ? FAIL : OK;
case VAR_FLOAT: case VAR_FLOAT:
#ifdef FEAT_FLOAT #ifdef FEAT_FLOAT
@@ -3196,12 +3209,16 @@ eval6(
else else
#endif #endif
{ {
int failed = FALSE;
if (op == '*') if (op == '*')
n1 = n1 * n2; n1 = n1 * n2;
else if (op == '/') else if (op == '/')
n1 = num_divide(n1, n2); n1 = num_divide(n1, n2, &failed);
else else
n1 = num_modulus(n1, n2); n1 = num_modulus(n1, n2, &failed);
if (failed)
return FAIL;
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n1; rettv->vval.v_number = n1;

View File

@@ -1410,8 +1410,10 @@ ex_let_one(
case '+': n = numval + n; break; case '+': n = numval + n; break;
case '-': n = numval - n; break; 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_divide(numval, n,
case '%': n = (long)num_modulus(numval, n); break; &failed); break;
case '%': n = (long)num_modulus(numval, n,
&failed); break;
} }
} }
else if (opt_type == gov_string else if (opt_type == gov_string

View File

@@ -1,6 +1,6 @@
/* eval.c */ /* eval.c */
varnumber_T num_divide(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); varnumber_T num_modulus(varnumber_T n1, varnumber_T n2, int *failed);
void eval_init(void); void eval_init(void);
void eval_clear(void); void eval_clear(void);
void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip); void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip);

View File

@@ -1489,6 +1489,30 @@ def Test_unlet()
assert_equal('', $ENVVAR) assert_equal('', $ENVVAR)
enddef 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() def Test_assign_command_modifier()
var lines =<< trim END var lines =<< trim END
var verbose = 0 var verbose = 0

View File

@@ -69,6 +69,19 @@ def CheckScriptFailure(lines: list<string>, error: string, lnum = -3)
endtry endtry
enddef 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>) def CheckScriptSuccess(lines: list<string>)
var cwd = getcwd() var cwd = getcwd()
var fname = 'XScriptSuccess' .. s:sequence var fname = 'XScriptSuccess' .. s:sequence

View File

@@ -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 */
/**/
2381,
/**/ /**/
2380, 2380,
/**/ /**/

View File

@@ -4291,9 +4291,10 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
&& ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER && ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER
&& ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER) && ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)
{ {
typval_T *tv1 = &ppconst->pp_tv[ppconst_used]; typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1]; typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
varnumber_T res = 0; varnumber_T res = 0;
int failed = FALSE;
// both are numbers: compute the result // both are numbers: compute the result
switch (*op) switch (*op)
@@ -4301,12 +4302,14 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
case '*': res = tv1->vval.v_number * tv2->vval.v_number; case '*': res = tv1->vval.v_number * tv2->vval.v_number;
break; break;
case '/': res = num_divide(tv1->vval.v_number, case '/': res = num_divide(tv1->vval.v_number,
tv2->vval.v_number); tv2->vval.v_number, &failed);
break; break;
case '%': res = num_modulus(tv1->vval.v_number, case '%': res = num_modulus(tv1->vval.v_number,
tv2->vval.v_number); tv2->vval.v_number, &failed);
break; break;
} }
if (failed)
return FAIL;
tv1->vval.v_number = res; tv1->vval.v_number = res;
--ppconst->pp_used; --ppconst->pp_used;
} }