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:
31
src/eval.c
31
src/eval.c
@@ -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;
|
||||||
|
@@ -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
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -4294,6 +4294,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user