summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/compile.c30
-rw-r--r--py/intdivmod.c24
-rw-r--r--py/intdivmod.h4
-rw-r--r--py/malloc.c6
-rw-r--r--py/misc.h1
-rw-r--r--py/obj.h1
-rw-r--r--py/objexcept.c3
-rw-r--r--py/objint_mpz.c6
-rw-r--r--py/py.mk2
-rw-r--r--py/runtime.c65
-rw-r--r--py/smallint.c53
-rw-r--r--py/smallint.h5
-rw-r--r--py/vm.c4
-rw-r--r--tests/basics/memoryerror.py6
-rw-r--r--tests/io/file-with.py9
-rw-r--r--tests/misc/rge-sm.py2
16 files changed, 125 insertions, 96 deletions
diff --git a/py/compile.c b/py/compile.c
index 3273abe40e..6ede842397 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -17,7 +17,7 @@
#include "obj.h"
#include "compile.h"
#include "runtime.h"
-#include "intdivmod.h"
+#include "smallint.h"
// TODO need to mangle __attr names
@@ -115,38 +115,38 @@ mp_parse_node_t fold_constants(mp_parse_node_t pn) {
if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
machine_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
machine_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]);
- machine_int_t res;
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PLUS)) {
- res = arg0 + arg1;
+ arg0 += arg1;
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_MINUS)) {
- res = arg0 - arg1;
+ arg0 -= arg1;
} else {
// shouldn't happen
assert(0);
- res = 0;
}
- if (MP_PARSE_FITS_SMALL_INT(res)) {
- pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, res);
+ if (MP_PARSE_FITS_SMALL_INT(arg0)) {
+ pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0);
}
}
break;
case PN_term:
if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
- int arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
- int arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]);
+ machine_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
+ machine_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]);
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) {
-#if MICROPY_EMIT_CPYTHON
- // can overflow; enabled only to compare with CPython
- pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 * arg1);
-#endif
+ if (!mp_small_int_mul_overflow(arg0, arg1)) {
+ arg0 *= arg1;
+ if (MP_PARSE_FITS_SMALL_INT(arg0)) {
+ pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0);
+ }
+ }
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_SLASH)) {
; // pass
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PERCENT)) {
- pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, python_modulo(arg0, arg1));
+ pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_modulo(arg0, arg1));
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_SLASH)) {
if (arg1 != 0) {
- pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, python_floor_divide(arg0, arg1));
+ pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_floor_divide(arg0, arg1));
}
} else {
// shouldn't happen
diff --git a/py/intdivmod.c b/py/intdivmod.c
deleted file mode 100644
index 4cb363b511..0000000000
--- a/py/intdivmod.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "mpconfig.h"
-
-machine_int_t python_modulo(machine_int_t dividend, machine_int_t divisor) {
- machine_int_t lsign = (dividend >= 0) ? 1 :-1;
- machine_int_t rsign = (divisor >= 0) ? 1 :-1;
- dividend %= divisor;
- if (lsign != rsign) {
- dividend += divisor;
- }
- return dividend;
-}
-
-
-machine_int_t python_floor_divide(machine_int_t num, machine_int_t denom) {
- machine_int_t lsign = num > 0 ? 1 : -1;
- machine_int_t rsign = denom > 0 ? 1 : -1;
- if (lsign == -1) {num *= -1;}
- if (rsign == -1) {denom *= -1;}
- if (lsign != rsign){
- return - ( num + denom - 1) / denom;
- } else {
- return num / denom;
- }
-}
diff --git a/py/intdivmod.h b/py/intdivmod.h
deleted file mode 100644
index 7716bd21e9..0000000000
--- a/py/intdivmod.h
+++ /dev/null
@@ -1,4 +0,0 @@
-// Functions for integer modulo and floor division
-
-machine_int_t python_modulo(machine_int_t dividend, machine_int_t divisor);
-machine_int_t python_floor_divide(machine_int_t num, machine_int_t denom);
diff --git a/py/malloc.c b/py/malloc.c
index 27eaac1088..4cf1b71db0 100644
--- a/py/malloc.c
+++ b/py/malloc.c
@@ -41,8 +41,7 @@ void *m_malloc(int num_bytes) {
}
void *ptr = malloc(num_bytes);
if (ptr == NULL) {
- printf("could not allocate memory, allocating %d bytes\n", num_bytes);
- return NULL;
+ return m_malloc_fail(num_bytes);
}
#if MICROPY_MEM_STATS
total_bytes_allocated += num_bytes;
@@ -68,8 +67,7 @@ void *m_realloc(void *ptr, int old_num_bytes, int new_num_bytes) {
}
void *new_ptr = realloc(ptr, new_num_bytes);
if (new_ptr == NULL) {
- printf("could not allocate memory, reallocating %d bytes\n", new_num_bytes);
- return NULL;
+ return m_malloc_fail(new_num_bytes);
}
#if MICROPY_MEM_STATS
// At first thought, "Total bytes allocated" should only grow,
diff --git a/py/misc.h b/py/misc.h
index 35081f18e9..662513eb1a 100644
--- a/py/misc.h
+++ b/py/misc.h
@@ -36,6 +36,7 @@ void *m_malloc(int num_bytes);
void *m_malloc0(int num_bytes);
void *m_realloc(void *ptr, int old_num_bytes, int new_num_bytes);
void m_free(void *ptr, int num_bytes);
+void *m_malloc_fail(int num_bytes);
int m_get_total_bytes_allocated(void);
int m_get_current_bytes_allocated(void);
diff --git a/py/obj.h b/py/obj.h
index f5ce873d6b..fe3c14d116 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -306,6 +306,7 @@ extern const struct _mp_obj_bool_t mp_const_false_obj;
extern const struct _mp_obj_bool_t mp_const_true_obj;
extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj;
extern const struct _mp_obj_ellipsis_t mp_const_ellipsis_obj;
+extern const struct _mp_obj_exception_t mp_const_MemoryError_obj;
extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj;
// General API for objects
diff --git a/py/objexcept.c b/py/objexcept.c
index 0efc21d360..11651025f0 100644
--- a/py/objexcept.c
+++ b/py/objexcept.c
@@ -17,6 +17,9 @@ typedef struct _mp_obj_exception_t {
mp_obj_tuple_t args;
} mp_obj_exception_t;
+// Instance of MemoryError exception - needed by mp_malloc_fail
+const mp_obj_exception_t mp_const_MemoryError_obj = {{&mp_type_MemoryError}, MP_OBJ_NULL, {{&mp_type_tuple}, 0}};
+
// Instance of GeneratorExit exception - needed by generator.close()
// This would belong to objgenerator.c, but to keep mp_obj_exception_t
// definition module-private so far, have it here.
diff --git a/py/objint_mpz.c b/py/objint_mpz.c
index 25a069cf93..d338428135 100644
--- a/py/objint_mpz.c
+++ b/py/objint_mpz.c
@@ -113,7 +113,7 @@ mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: {
mpz_t rem; mpz_init_zero(&rem);
mpz_divmod_inpl(&res->mpz, &rem, zlhs, zrhs);
- if (zlhs->neg != zrhs->neg) {
+ if (zlhs->neg != zrhs->neg) {
if (!mpz_is_zero(&rem)) {
mpz_t mpzone; mpz_init_from_int(&mpzone, -1);
mpz_add_inpl(&res->mpz, &res->mpz, &mpzone);
@@ -127,8 +127,8 @@ mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
mpz_t quo; mpz_init_zero(&quo);
mpz_divmod_inpl(&quo, &res->mpz, zlhs, zrhs);
mpz_deinit(&quo);
- // Check signs and do Python style modulo
- if (zlhs->neg != zrhs->neg) {
+ // Check signs and do Python style modulo
+ if (zlhs->neg != zrhs->neg) {
mpz_add_inpl(&res->mpz, &res->mpz, zrhs);
}
break;
diff --git a/py/py.mk b/py/py.mk
index 059029274e..a08997332b 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -84,7 +84,7 @@ PY_O_BASENAME = \
vm.o \
showbc.o \
repl.o \
- intdivmod.o \
+ smallint.o \
pfenv.o \
# prepend the build destination prefix to the py object files
diff --git a/py/runtime.c b/py/runtime.c
index d9e2298c4c..a5f45a2fb4 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -16,7 +16,7 @@
#include "builtin.h"
#include "builtintables.h"
#include "bc.h"
-#include "intdivmod.h"
+#include "smallint.h"
#include "objgenerator.h"
#if 0 // print debugging info
@@ -289,7 +289,7 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
// If long long type exists and is larger than machine_int_t, then
// we can use the following code to perform overflow-checked multiplication.
- // Otherwise (eg in x64 case) we must use the branching code below.
+ // Otherwise (eg in x64 case) we must use mp_small_int_mul_overflow.
#if 0
// compute result using long long precision
long long res = (long long)lhs_val * (long long)rhs_val;
@@ -302,36 +302,14 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
}
#endif
- if (lhs_val > 0) { // lhs_val is positive
- if (rhs_val > 0) { // lhs_val and rhs_val are positive
- if (lhs_val > (MP_SMALL_INT_MAX / rhs_val)) {
- goto mul_overflow;
- }
- } else { // lhs_val positive, rhs_val nonpositive
- if (rhs_val < (MP_SMALL_INT_MIN / lhs_val)) {
- goto mul_overflow;
- }
- } // lhs_val positive, rhs_val nonpositive
- } else { // lhs_val is nonpositive
- if (rhs_val > 0) { // lhs_val is nonpositive, rhs_val is positive
- if (lhs_val < (MP_SMALL_INT_MIN / rhs_val)) {
- goto mul_overflow;
- }
- } else { // lhs_val and rhs_val are nonpositive
- if (lhs_val != 0 && rhs_val < (MP_SMALL_INT_MAX / lhs_val)) {
- goto mul_overflow;
- }
- } // End if lhs_val and rhs_val are nonpositive
- } // End if lhs_val is nonpositive
-
- // use standard precision
- return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val);
-
- mul_overflow:
- // use higher precision
- lhs = mp_obj_new_int_from_ll(lhs_val);
- goto generic_binary_op;
-
+ if (mp_small_int_mul_overflow(lhs_val, rhs_val)) {
+ // use higher precision
+ lhs = mp_obj_new_int_from_ll(lhs_val);
+ goto generic_binary_op;
+ } else {
+ // use standard precision
+ return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val);
+ }
break;
}
case MP_BINARY_OP_FLOOR_DIVIDE:
@@ -339,7 +317,7 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
if (rhs_val == 0) {
goto zero_division;
}
- lhs_val = python_floor_divide(lhs_val, rhs_val);
+ lhs_val = mp_small_int_floor_divide(lhs_val, rhs_val);
break;
#if MICROPY_ENABLE_FLOAT
@@ -352,11 +330,11 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
#endif
case MP_BINARY_OP_MODULO:
- case MP_BINARY_OP_INPLACE_MODULO:
- {
- lhs_val = python_modulo(lhs_val, rhs_val);
+ case MP_BINARY_OP_INPLACE_MODULO: {
+ lhs_val = mp_small_int_modulo(lhs_val, rhs_val);
break;
}
+
case MP_BINARY_OP_POWER:
case MP_BINARY_OP_INPLACE_POWER:
if (rhs_val < 0) {
@@ -370,21 +348,19 @@ mp_obj_t mp_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
machine_int_t ans = 1;
while (rhs_val > 0) {
if (rhs_val & 1) {
- machine_int_t old = ans;
- ans *= lhs_val;
- if (ans < old) {
+ if (mp_small_int_mul_overflow(ans, lhs_val)) {
goto power_overflow;
}
+ ans *= lhs_val;
}
if (rhs_val == 1) {
break;
}
rhs_val /= 2;
- machine_int_t old = lhs_val;
- lhs_val *= lhs_val;
- if (lhs_val < old) {
+ if (mp_small_int_mul_overflow(lhs_val, lhs_val)) {
goto power_overflow;
}
+ lhs_val *= lhs_val;
}
lhs_val = ans;
}
@@ -1034,6 +1010,11 @@ void mp_globals_set(mp_map_t *m) {
map_globals = m;
}
+void *m_malloc_fail(int num_bytes) {
+ DEBUG_printf("memory allocation failed, allocating %d bytes\n", num_bytes);
+ nlr_jump((mp_obj_t)&mp_const_MemoryError_obj);
+}
+
// these must correspond to the respective enum
void *const mp_fun_table[MP_F_NUMBER_OF] = {
mp_load_const_dec,
diff --git a/py/smallint.c b/py/smallint.c
new file mode 100644
index 0000000000..ac0cf2f5f9
--- /dev/null
+++ b/py/smallint.c
@@ -0,0 +1,53 @@
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+
+bool mp_small_int_mul_overflow(machine_int_t x, machine_int_t y) {
+ // Check for multiply overflow; see CERT INT32-C
+ if (x > 0) { // x is positive
+ if (y > 0) { // x and y are positive
+ if (x > (MP_SMALL_INT_MAX / y)) {
+ return true;
+ }
+ } else { // x positive, y nonpositive
+ if (y < (MP_SMALL_INT_MIN / x)) {
+ return true;
+ }
+ } // x positive, y nonpositive
+ } else { // x is nonpositive
+ if (y > 0) { // x is nonpositive, y is positive
+ if (x < (MP_SMALL_INT_MIN / y)) {
+ return true;
+ }
+ } else { // x and y are nonpositive
+ if (x != 0 && y < (MP_SMALL_INT_MAX / x)) {
+ return true;
+ }
+ } // End if x and y are nonpositive
+ } // End if x is nonpositive
+ return false;
+}
+
+machine_int_t mp_small_int_modulo(machine_int_t dividend, machine_int_t divisor) {
+ machine_int_t lsign = (dividend >= 0) ? 1 :-1;
+ machine_int_t rsign = (divisor >= 0) ? 1 :-1;
+ dividend %= divisor;
+ if (lsign != rsign) {
+ dividend += divisor;
+ }
+ return dividend;
+}
+
+
+machine_int_t mp_small_int_floor_divide(machine_int_t num, machine_int_t denom) {
+ machine_int_t lsign = num > 0 ? 1 : -1;
+ machine_int_t rsign = denom > 0 ? 1 : -1;
+ if (lsign == -1) {num *= -1;}
+ if (rsign == -1) {denom *= -1;}
+ if (lsign != rsign){
+ return - ( num + denom - 1) / denom;
+ } else {
+ return num / denom;
+ }
+}
diff --git a/py/smallint.h b/py/smallint.h
new file mode 100644
index 0000000000..14234fa7b8
--- /dev/null
+++ b/py/smallint.h
@@ -0,0 +1,5 @@
+// Functions for small integer arithmetic
+
+bool mp_small_int_mul_overflow(machine_int_t x, machine_int_t y);
+machine_int_t mp_small_int_modulo(machine_int_t dividend, machine_int_t divisor);
+machine_int_t mp_small_int_floor_divide(machine_int_t num, machine_int_t denom);
diff --git a/py/vm.c b/py/vm.c
index 69f350f9fd..6186bbcefb 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -849,8 +849,8 @@ yield:
// set file and line number that the exception occurred at
// TODO: don't set traceback for exceptions re-raised by END_FINALLY.
// But consider how to handle nested exceptions.
- // TODO need a better way of not adding traceback to constant objects (right now, just GeneratorExit_obj)
- if (mp_obj_is_exception_instance(nlr.ret_val) && nlr.ret_val != &mp_const_GeneratorExit_obj) {
+ // TODO need a better way of not adding traceback to constant objects (right now, just GeneratorExit_obj and MemoryError_obj)
+ if (mp_obj_is_exception_instance(nlr.ret_val) && nlr.ret_val != &mp_const_GeneratorExit_obj && nlr.ret_val != &mp_const_MemoryError_obj) {
machine_uint_t code_info_size = code_info[0] | (code_info[1] << 8) | (code_info[2] << 16) | (code_info[3] << 24);
qstr source_file = code_info[4] | (code_info[5] << 8) | (code_info[6] << 16) | (code_info[7] << 24);
qstr block_name = code_info[8] | (code_info[9] << 8) | (code_info[10] << 16) | (code_info[11] << 24);
diff --git a/tests/basics/memoryerror.py b/tests/basics/memoryerror.py
new file mode 100644
index 0000000000..b4be420c31
--- /dev/null
+++ b/tests/basics/memoryerror.py
@@ -0,0 +1,6 @@
+l = list(range(10000))
+try:
+ 100000000 * l
+except MemoryError:
+ print('MemoryError')
+print(len(l), l[0], l[-1])
diff --git a/tests/io/file-with.py b/tests/io/file-with.py
index 2350721641..ee1e702422 100644
--- a/tests/io/file-with.py
+++ b/tests/io/file-with.py
@@ -10,3 +10,12 @@ except:
# Note: CPython and us throw different exception trying to read from
# close file.
print("can't read file after with")
+
+
+# Regression test: test that exception in with initialization properly
+# thrown and doesn't crash.
+try:
+ with open('__non_existent', 'r'):
+ pass
+except OSError:
+ print("OSError")
diff --git a/tests/misc/rge-sm.py b/tests/misc/rge-sm.py
index 40c6029b47..1860f7a318 100644
--- a/tests/misc/rge-sm.py
+++ b/tests/misc/rge-sm.py
@@ -111,4 +111,4 @@ def singleTraj(system, trajStart, h=0.02, tend=1.0):
#phaseDiagram(sysSM, (lambda i, j: [0.354, 0.654, 1.278, 0.8 + 0.2 * i, 0.1 + 0.1 * j]), (lambda a: (a[4], a[5])), h=0.1, tend=math.log(10**17))
# initial conditions at M_Z
-singleTraj(sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.1, tend=math.log(10**17)) # true values
+singleTraj(sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10**17)) # true values