Skip to content

Commit 8270e38

Browse files
committed
py: More robust int conversion and overflow checking.
1 parent a58a7ae commit 8270e38

6 files changed

Lines changed: 63 additions & 9 deletions

File tree

py/mpz.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,7 @@ mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs) {
11391139
}
11401140
#endif
11411141

1142+
// TODO check that this correctly handles overflow in all cases
11421143
machine_int_t mpz_as_int(const mpz_t *i) {
11431144
machine_int_t val = 0;
11441145
mpz_dig_t *d = i->dig + i->len;
@@ -1147,11 +1148,13 @@ machine_int_t mpz_as_int(const mpz_t *i) {
11471148
machine_int_t oldval = val;
11481149
val = (val << DIG_SIZE) | *d;
11491150
if (val < oldval) {
1150-
// TODO need better handling of conversion overflow
1151+
// overflow, return +/- "infinity"
11511152
if (i->neg == 0) {
1152-
return 0x7fffffff;
1153+
// +infinity
1154+
return ~WORD_MSBIT_HIGH;
11531155
} else {
1154-
return 0x80000000;
1156+
// -infinity
1157+
return WORD_MSBIT_HIGH;
11551158
}
11561159
}
11571160
}
@@ -1163,6 +1166,28 @@ machine_int_t mpz_as_int(const mpz_t *i) {
11631166
return val;
11641167
}
11651168

1169+
// TODO check that this correctly handles overflow in all cases
1170+
bool mpz_as_int_checked(const mpz_t *i, machine_int_t *value) {
1171+
machine_int_t val = 0;
1172+
mpz_dig_t *d = i->dig + i->len;
1173+
1174+
while (--d >= i->dig) {
1175+
machine_int_t oldval = val;
1176+
val = (val << DIG_SIZE) | *d;
1177+
if (val < oldval) {
1178+
// overflow
1179+
return false;
1180+
}
1181+
}
1182+
1183+
if (i->neg != 0) {
1184+
val = -val;
1185+
}
1186+
1187+
*value = val;
1188+
return true;
1189+
}
1190+
11661191
#if MICROPY_ENABLE_FLOAT
11671192
mp_float_t mpz_as_float(const mpz_t *i) {
11681193
mp_float_t val = 0;

py/mpz.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ mpz_t *mpz_div(const mpz_t *lhs, const mpz_t *rhs);
7171
mpz_t *mpz_mod(const mpz_t *lhs, const mpz_t *rhs);
7272

7373
machine_int_t mpz_as_int(const mpz_t *z);
74+
bool mpz_as_int_checked(const mpz_t *z, machine_int_t *value);
7475
#if MICROPY_ENABLE_FLOAT
7576
mp_float_t mpz_as_float(const mpz_t *z);
7677
#endif

py/obj.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,24 @@ machine_int_t mp_obj_get_int(mp_obj_t arg) {
203203
}
204204
}
205205

206+
// returns false if arg is not of integral type
207+
// returns true and sets *value if it is of integral type
208+
// can throw OverflowError if arg is of integral type, but doesn't fit in a machine_int_t
209+
bool mp_obj_get_int_maybe(mp_obj_t arg, machine_int_t *value) {
210+
if (arg == mp_const_false) {
211+
*value = 0;
212+
} else if (arg == mp_const_true) {
213+
*value = 1;
214+
} else if (MP_OBJ_IS_SMALL_INT(arg)) {
215+
*value = MP_OBJ_SMALL_INT_VALUE(arg);
216+
} else if (MP_OBJ_IS_TYPE(arg, &mp_type_int)) {
217+
*value = mp_obj_int_get_checked(arg);
218+
} else {
219+
return false;
220+
}
221+
return true;
222+
}
223+
206224
#if MICROPY_ENABLE_FLOAT
207225
mp_float_t mp_obj_get_float(mp_obj_t arg) {
208226
if (arg == mp_const_false) {

py/obj.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2);
359359
bool mp_obj_less(mp_obj_t o1, mp_obj_t o2);
360360

361361
machine_int_t mp_obj_get_int(mp_obj_t arg);
362+
bool mp_obj_get_int_maybe(mp_obj_t arg, machine_int_t *value);
362363
#if MICROPY_ENABLE_FLOAT
363364
mp_float_t mp_obj_get_float(mp_obj_t self_in);
364365
void mp_obj_get_complex(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag);

py/objint_mpz.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,18 @@ machine_int_t mp_obj_int_get(mp_obj_t self_in) {
243243
}
244244

245245
machine_int_t mp_obj_int_get_checked(mp_obj_t self_in) {
246-
// TODO: Check overflow
247-
return mp_obj_int_get(self_in);
246+
if (MP_OBJ_IS_SMALL_INT(self_in)) {
247+
return MP_OBJ_SMALL_INT_VALUE(self_in);
248+
} else {
249+
mp_obj_int_t *self = self_in;
250+
machine_int_t value;
251+
if (mpz_as_int_checked(&self->mpz, &value)) {
252+
return value;
253+
} else {
254+
// overflow
255+
nlr_jump(mp_obj_new_exception_msg(&mp_type_OverflowError, "overflow converting long int to machine word"));
256+
}
257+
}
248258
}
249259

250260
#if MICROPY_ENABLE_FLOAT

py/objlist.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,11 @@ STATIC mp_obj_t list_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
122122
list_extend(lhs, rhs);
123123
return o;
124124
}
125-
case MP_BINARY_OP_MULTIPLY:
126-
{
127-
if (!MP_OBJ_IS_SMALL_INT(rhs)) {
125+
case MP_BINARY_OP_MULTIPLY: {
126+
machine_int_t n;
127+
if (!mp_obj_get_int_maybe(rhs, &n)) {
128128
return NULL;
129129
}
130-
int n = MP_OBJ_SMALL_INT_VALUE(rhs);
131130
mp_obj_list_t *s = list_new(o->len * n);
132131
mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items);
133132
return s;

0 commit comments

Comments
 (0)