aboutsummaryrefslogtreecommitdiff
path: root/py/nativeglue.c
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2018-09-13 22:03:48 +1000
committerDamien George <damien.p.george@gmail.com>2018-09-13 22:47:20 +1000
commit4f3d9429b54ccc2d36123c861cd916b1ee15c640 (patch)
tree864a80337a727a02085128882fea454a0d0700d5 /py/nativeglue.c
parent9fb1f18cf450216e30d28ccd246a596555b09f87 (diff)
py: Fix native functions so they run with their correct globals context.
Prior to this commit a function compiled with the native decorator @micropython.native would not work correctly when accessing global variables, because the globals dict was not being set upon function entry. This commit fixes this problem by, upon function entry, setting as the current globals dict the globals dict context the function was defined within, as per normal Python semantics, and as bytecode does. Upon function exit the original globals dict is restored. In order to restore the globals dict when an exception is raised the native function must guard its internals with an nlr_push/nlr_pop pair. Because this push/pop is relatively expensive, in both C stack usage for the nlr_buf_t and CPU execution time, the implementation here optimises things as much as possible. First, the compiler keeps track of whether a function even needs to access global variables. Using this information the native emitter then generates three different kinds of code: 1. no globals used, no exception handlers: no nlr handling code and no setting of the globals dict. 2. globals used, no exception handlers: an nlr_buf_t is allocated on the C stack but it is not used if the globals dict is unchanged, saving execution time because nlr_push/nlr_pop don't need to run. 3. function has exception handlers, may use globals: an nlr_buf_t is allocated and nlr_push/nlr_pop are always called. In the end, native functions that don't access globals and don't have exception handlers will run more efficiently than those that do. Fixes issue #1573.
Diffstat (limited to 'py/nativeglue.c')
-rw-r--r--py/nativeglue.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/py/nativeglue.c b/py/nativeglue.c
index b87da6931..7ff8273f9 100644
--- a/py/nativeglue.c
+++ b/py/nativeglue.c
@@ -83,6 +83,20 @@ mp_obj_t mp_convert_native_to_obj(mp_uint_t val, mp_uint_t type) {
#if MICROPY_EMIT_NATIVE
+mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals) {
+ if (new_globals == NULL) {
+ // Globals were the originally the same so don't restore them
+ return NULL;
+ }
+ mp_obj_dict_t *old_globals = mp_globals_get();
+ if (old_globals == new_globals) {
+ // Don't set globals if they are the same, and return NULL to indicate this
+ return NULL;
+ }
+ mp_globals_set(new_globals);
+ return old_globals;
+}
+
// wrapper that accepts n_args and n_kw in one argument
// (native emitter can only pass at most 3 arguments to a function)
mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args) {
@@ -127,6 +141,7 @@ STATIC mp_obj_t mp_native_iternext(mp_obj_iter_buf_t *iter) {
void *const mp_fun_table[MP_F_NUMBER_OF] = {
mp_convert_obj_to_native,
mp_convert_native_to_obj,
+ mp_native_swap_globals,
mp_load_name,
mp_load_global,
mp_load_build_class,