aboutsummaryrefslogtreecommitdiffstats
path: root/src/data.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/data.c')
-rw-r--r--src/data.c36
1 files changed, 34 insertions, 2 deletions
diff --git a/src/data.c b/src/data.c
index 577ae777d8..073fb0d492 100644
--- a/src/data.c
+++ b/src/data.c
@@ -22,6 +22,9 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <signal.h>
#include <stdio.h>
#include <setjmp.h>
+
+#include <intprops.h>
+
#include "lisp.h"
#include "puresize.h"
#include "character.h"
@@ -2431,6 +2434,10 @@ arith_driver (enum arithop code, size_t nargs, register Lisp_Object *args)
register EMACS_INT accum = 0;
register EMACS_INT next;
+ int overflow = 0;
+ size_t ok_args;
+ EMACS_INT ok_accum;
+
switch (SWITCH_ENUM_CAST (code))
{
case Alogior:
@@ -2451,25 +2458,47 @@ arith_driver (enum arithop code, size_t nargs, register Lisp_Object *args)
for (argnum = 0; argnum < nargs; argnum++)
{
+ if (! overflow)
+ {
+ ok_args = argnum;
+ ok_accum = accum;
+ }
+
/* Using args[argnum] as argument to CHECK_NUMBER_... */
val = args[argnum];
CHECK_NUMBER_OR_FLOAT_COERCE_MARKER (val);
if (FLOATP (val))
- return float_arith_driver ((double) accum, argnum, code,
+ return float_arith_driver (ok_accum, ok_args, code,
nargs, args);
args[argnum] = val;
next = XINT (args[argnum]);
switch (SWITCH_ENUM_CAST (code))
{
case Aadd:
+ if (INT_ADD_OVERFLOW (accum, next))
+ {
+ overflow = 1;
+ accum &= INTMASK;
+ }
accum += next;
break;
case Asub:
+ if (INT_SUBTRACT_OVERFLOW (accum, next))
+ {
+ overflow = 1;
+ accum &= INTMASK;
+ }
accum = argnum ? accum - next : nargs == 1 ? - next : next;
break;
case Amult:
- accum *= next;
+ if (INT_MULTIPLY_OVERFLOW (accum, next))
+ {
+ overflow = 1;
+ accum = (EMACS_UINT) accum * (EMACS_UINT) next & INTMASK;
+ }
+ else
+ accum *= next;
break;
case Adiv:
if (!argnum)
@@ -2501,6 +2530,9 @@ arith_driver (enum arithop code, size_t nargs, register Lisp_Object *args)
}
}
+ accum &= INTMASK;
+ if (MOST_POSITIVE_FIXNUM < accum)
+ accum += MOST_NEGATIVE_FIXNUM - (MOST_POSITIVE_FIXNUM + 1);
XSETINT (val, accum);
return val;
}