Skip to content

Commit c4d0868

Browse files
committed
py: Implement proper context save/restore for eval/exec; factor code.
This has benefits all round: code factoring for parse/compile/execute, proper context save/restore for exec, allow to sepcify globals/locals for eval, and reduced ROM usage by >100 bytes on stmhal and unix. Also, the call to mp_parse_compile_execute is tail call optimised for the import code, so it doesn't increase stack memory usage.
1 parent a91ac20 commit c4d0868

File tree

4 files changed

+76
-95
lines changed

4 files changed

+76
-95
lines changed

py/builtinevex.c

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -40,66 +40,34 @@
4040
#include "runtime.h"
4141
#include "builtin.h"
4242

43-
STATIC mp_obj_t parse_compile_execute(mp_obj_t o_in, mp_parse_input_kind_t parse_input_kind) {
43+
STATIC mp_obj_t eval_exec_helper(mp_uint_t n_args, const mp_obj_t *args, mp_parse_input_kind_t parse_input_kind) {
4444
mp_uint_t str_len;
45-
const char *str = mp_obj_str_get_data(o_in, &str_len);
45+
const char *str = mp_obj_str_get_data(args[0], &str_len);
4646

4747
// create the lexer
4848
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, str, str_len, 0);
49-
qstr source_name = mp_lexer_source_name(lex);
5049

51-
// parse the string
52-
mp_parse_error_kind_t parse_error_kind;
53-
mp_parse_node_t pn = mp_parse(lex, parse_input_kind, &parse_error_kind);
54-
55-
if (pn == MP_PARSE_NODE_NULL) {
56-
// parse error; raise exception
57-
mp_obj_t exc = mp_parse_make_exception(lex, parse_error_kind);
58-
mp_lexer_free(lex);
59-
nlr_raise(exc);
60-
}
61-
62-
mp_lexer_free(lex);
63-
64-
// compile the string
65-
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false);
66-
67-
// check if there was a compile error
68-
if (mp_obj_is_exception_instance(module_fun)) {
69-
nlr_raise(module_fun);
70-
}
71-
72-
// complied successfully, execute it
73-
return mp_call_function_0(module_fun);
74-
}
75-
76-
STATIC mp_obj_t mp_builtin_eval(mp_obj_t o_in) {
77-
return parse_compile_execute(o_in, MP_PARSE_EVAL_INPUT);
78-
}
79-
80-
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_eval_obj, mp_builtin_eval);
81-
82-
STATIC mp_obj_t mp_builtin_exec(uint n_args, const mp_obj_t *args) {
83-
// Unconditional getting/setting assumes that these operations
84-
// are cheap, which is the case when this comment was written.
85-
mp_obj_dict_t *old_globals = mp_globals_get();
86-
mp_obj_dict_t *old_locals = mp_locals_get();
50+
// work out the context
51+
mp_obj_dict_t *globals = mp_globals_get();
52+
mp_obj_dict_t *locals = mp_locals_get();
8753
if (n_args > 1) {
88-
mp_obj_t globals = args[1];
89-
mp_obj_t locals;
54+
globals = args[1];
9055
if (n_args > 2) {
9156
locals = args[2];
9257
} else {
9358
locals = globals;
9459
}
95-
mp_globals_set(globals);
96-
mp_locals_set(locals);
9760
}
98-
mp_obj_t res = parse_compile_execute(args[0], MP_PARSE_FILE_INPUT);
99-
// TODO if the above call throws an exception, then we never get to reset the globals/locals
100-
mp_globals_set(old_globals);
101-
mp_locals_set(old_locals);
102-
return res;
61+
62+
return mp_parse_compile_execute(lex, parse_input_kind, globals, locals);
63+
}
64+
65+
STATIC mp_obj_t mp_builtin_eval(mp_uint_t n_args, const mp_obj_t *args) {
66+
return eval_exec_helper(n_args, args, MP_PARSE_EVAL_INPUT);
10367
}
68+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_eval_obj, 1, 3, mp_builtin_eval);
10469

70+
STATIC mp_obj_t mp_builtin_exec(mp_uint_t n_args, const mp_obj_t *args) {
71+
return eval_exec_helper(n_args, args, MP_PARSE_FILE_INPUT);
72+
}
10573
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_exec_obj, 1, 3, mp_builtin_exec);

py/builtinimport.c

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -111,56 +111,14 @@ STATIC void do_load(mp_obj_t module_obj, vstr_t *file) {
111111
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ImportError, "No module named '%s'", vstr_str(file)));
112112
}
113113

114-
qstr source_name = mp_lexer_source_name(lex);
115-
116-
// save the old context
117-
mp_obj_dict_t *old_locals = mp_locals_get();
118-
mp_obj_dict_t *old_globals = mp_globals_get();
119-
120-
// set the new context
121-
mp_locals_set(mp_obj_module_get_globals(module_obj));
122-
mp_globals_set(mp_obj_module_get_globals(module_obj));
123114
#if MICROPY_PY___FILE__
124-
mp_store_attr(module_obj, MP_QSTR___file__, mp_obj_new_str(vstr_str(file), vstr_len(file), false));
115+
qstr source_name = mp_lexer_source_name(lex);
116+
mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
125117
#endif
126118

127-
// parse the imported script
128-
mp_parse_error_kind_t parse_error_kind;
129-
mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT, &parse_error_kind);
130-
131-
if (pn == MP_PARSE_NODE_NULL) {
132-
// parse error; clean up and raise exception
133-
mp_obj_t exc = mp_parse_make_exception(lex, parse_error_kind);
134-
mp_lexer_free(lex);
135-
mp_locals_set(old_locals);
136-
mp_globals_set(old_globals);
137-
nlr_raise(exc);
138-
}
139-
140-
mp_lexer_free(lex);
141-
142-
// compile the imported script
143-
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false);
144-
145-
if (mp_obj_is_exception_instance(module_fun)) {
146-
mp_locals_set(old_locals);
147-
mp_globals_set(old_globals);
148-
nlr_raise(module_fun);
149-
}
150-
151-
// complied successfully, execute it
152-
nlr_buf_t nlr;
153-
if (nlr_push(&nlr) == 0) {
154-
mp_call_function_0(module_fun);
155-
nlr_pop();
156-
} else {
157-
// exception; restore context and re-raise same exception
158-
mp_locals_set(old_locals);
159-
mp_globals_set(old_globals);
160-
nlr_raise(nlr.ret_val);
161-
}
162-
mp_locals_set(old_locals);
163-
mp_globals_set(old_globals);
119+
// parse, compile and execute the module in its context
120+
mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj);
121+
mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals);
164122
}
165123

166124
mp_obj_t mp_builtin___import__(mp_uint_t n_args, mp_obj_t *args) {

py/compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ enum {
3535

3636
// the compiler will free the parse tree (pn) before it returns
3737
mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is_repl);
38+
39+
// this is implemented in runtime.c
40+
mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals);

py/runtime.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* THE SOFTWARE.
2525
*/
2626

27+
#include <stdint.h>
2728
#include <stdio.h>
2829
#include <string.h>
2930
#include <assert.h>
@@ -46,6 +47,9 @@
4647
#include "smallint.h"
4748
#include "objgenerator.h"
4849
#include "lexer.h"
50+
#include "parse.h"
51+
#include "parsehelper.h"
52+
#include "compile.h"
4953
#include "stackctrl.h"
5054

5155
#if 0 // print debugging info
@@ -1153,6 +1157,54 @@ void mp_globals_set(mp_obj_dict_t *d) {
11531157
dict_globals = d;
11541158
}
11551159

1160+
// this is implemented in this file so it can optimise access to locals/globals
1161+
mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals) {
1162+
// parse the string
1163+
mp_parse_error_kind_t parse_error_kind;
1164+
mp_parse_node_t pn = mp_parse(lex, parse_input_kind, &parse_error_kind);
1165+
1166+
if (pn == MP_PARSE_NODE_NULL) {
1167+
// parse error; raise exception
1168+
mp_obj_t exc = mp_parse_make_exception(lex, parse_error_kind);
1169+
mp_lexer_free(lex);
1170+
nlr_raise(exc);
1171+
}
1172+
1173+
qstr source_name = mp_lexer_source_name(lex);
1174+
mp_lexer_free(lex);
1175+
1176+
// save context and set new context
1177+
mp_obj_dict_t *old_globals = mp_globals_get();
1178+
mp_obj_dict_t *old_locals = mp_locals_get();
1179+
mp_globals_set(globals);
1180+
mp_locals_set(locals);
1181+
1182+
// compile the string
1183+
mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false);
1184+
1185+
// check if there was a compile error
1186+
if (mp_obj_is_exception_instance(module_fun)) {
1187+
mp_globals_set(old_globals);
1188+
mp_locals_set(old_locals);
1189+
nlr_raise(module_fun);
1190+
}
1191+
1192+
// complied successfully, execute it
1193+
nlr_buf_t nlr;
1194+
if (nlr_push(&nlr) == 0) {
1195+
mp_obj_t ret = mp_call_function_0(module_fun);
1196+
nlr_pop();
1197+
mp_globals_set(old_globals);
1198+
mp_locals_set(old_locals);
1199+
return ret;
1200+
} else {
1201+
// exception; restore context and re-raise same exception
1202+
mp_globals_set(old_globals);
1203+
mp_locals_set(old_locals);
1204+
nlr_raise(nlr.ret_val);
1205+
}
1206+
}
1207+
11561208
void *m_malloc_fail(size_t num_bytes) {
11571209
DEBUG_printf("memory allocation failed, allocating " UINT_FMT " bytes\n", num_bytes);
11581210
nlr_raise((mp_obj_t)&mp_const_MemoryError_obj);

0 commit comments

Comments
 (0)