diff --git a/py/builtin.h b/py/builtin.h index 6b44a16ea8..762e10d9d9 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -35,6 +35,7 @@ MP_DECLARE_CONST_FUN_OBJ(mp_builtin_all_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_any_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_bin_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_callable_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_builtin_compile_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_chr_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_dir_obj); MP_DECLARE_CONST_FUN_OBJ(mp_builtin_divmod_obj); diff --git a/py/builtinevex.c b/py/builtinevex.c index e24055316e..73f254c5c2 100644 --- a/py/builtinevex.c +++ b/py/builtinevex.c @@ -34,19 +34,87 @@ #include "lexerunix.h" #include "parse.h" #include "obj.h" +#include "objfun.h" #include "parsehelper.h" #include "compile.h" #include "runtime0.h" #include "runtime.h" #include "builtin.h" -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) { +#if MICROPY_PY_BUILTINS_COMPILE + +typedef struct _mp_obj_code_t { + mp_obj_base_t base; + mp_obj_t module_fun; +} mp_obj_code_t; + +STATIC const mp_obj_type_t mp_type_code = { + { &mp_type_type }, + .name = MP_QSTR_code, +}; + +STATIC mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_t globals, mp_obj_t locals) { + // save context and set new context + mp_obj_dict_t *old_globals = mp_globals_get(); + mp_obj_dict_t *old_locals = mp_locals_get(); + mp_globals_set(globals); + mp_locals_set(locals); + + // a bit of a hack: fun_bc will re-set globals, so need to make sure it's + // the correct one + if (MP_OBJ_IS_TYPE(self->module_fun, &mp_type_fun_bc)) { + mp_obj_fun_bc_t *fun_bc = self->module_fun; + fun_bc->globals = globals; + } + + // execute code + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t ret = mp_call_function_0(self->module_fun); + nlr_pop(); + mp_globals_set(old_globals); + mp_locals_set(old_locals); + return ret; + } else { + // exception; restore context and re-raise same exception + mp_globals_set(old_globals); + mp_locals_set(old_locals); + nlr_raise(nlr.ret_val); + } +} + +STATIC mp_obj_t mp_builtin_compile(mp_uint_t n_args, const mp_obj_t *args) { + // get the source mp_uint_t str_len; const char *str = mp_obj_str_get_data(args[0], &str_len); - // create the lexer - mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, str, str_len, 0); + // get the filename + qstr filename = mp_obj_str_get_qstr(args[1]); + // create the lexer + mp_lexer_t *lex = mp_lexer_new_from_str_len(filename, str, str_len, 0); + + // get the compile mode + qstr mode = mp_obj_str_get_qstr(args[2]); + mp_parse_input_kind_t parse_input_kind; + switch (mode) { + case MP_QSTR_single: parse_input_kind = MP_PARSE_SINGLE_INPUT; break; + case MP_QSTR_exec: parse_input_kind = MP_PARSE_FILE_INPUT; break; + case MP_QSTR_eval: parse_input_kind = MP_PARSE_EVAL_INPUT; break; + default: + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "bad compile mode")); + } + + mp_obj_code_t *code = m_new_obj(mp_obj_code_t); + code->base.type = &mp_type_code; + code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL); + return code; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile); + +#endif // MICROPY_PY_BUILTINS_COMPILE + +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) { // work out the context mp_obj_dict_t *globals = mp_globals_get(); mp_obj_dict_t *locals = mp_locals_get(); @@ -59,6 +127,18 @@ STATIC mp_obj_t eval_exec_helper(mp_uint_t n_args, const mp_obj_t *args, mp_pars } } + #if MICROPY_PY_BUILTINS_COMPILE + if (MP_OBJ_IS_TYPE(args[0], &mp_type_code)) { + return code_execute(args[0], globals, locals); + } + #endif + + mp_uint_t str_len; + const char *str = mp_obj_str_get_data(args[0], &str_len); + + // create the lexer + mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, str, str_len, 0); + return mp_parse_compile_execute(lex, parse_input_kind, globals, locals); } diff --git a/py/builtintables.c b/py/builtintables.c index c8dea6bc76..2a5ced52d9 100644 --- a/py/builtintables.c +++ b/py/builtintables.c @@ -91,6 +91,9 @@ STATIC const mp_map_elem_t mp_builtin_object_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&mp_builtin_any_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_bin), (mp_obj_t)&mp_builtin_bin_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_callable), (mp_obj_t)&mp_builtin_callable_obj }, +#if MICROPY_PY_BUILTINS_COMPILE + { MP_OBJ_NEW_QSTR(MP_QSTR_compile), (mp_obj_t)&mp_builtin_compile_obj }, +#endif { MP_OBJ_NEW_QSTR(MP_QSTR_chr), (mp_obj_t)&mp_builtin_chr_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_dir), (mp_obj_t)&mp_builtin_dir_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_divmod), (mp_obj_t)&mp_builtin_divmod_obj }, diff --git a/py/mpconfig.h b/py/mpconfig.h index d24e6b23b8..b9e87fc012 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -312,6 +312,11 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_PROPERTY (1) #endif +// Whether to support compile function +#ifndef MICROPY_PY_BUILTINS_COMPILE +#define MICROPY_PY_BUILTINS_COMPILE (0) +#endif + // Whether to set __file__ for imported modules #ifndef MICROPY_PY___FILE__ #define MICROPY_PY___FILE__ (1) diff --git a/py/qstrdefs.h b/py/qstrdefs.h index e8a493dbe4..c87a7a83c1 100644 --- a/py/qstrdefs.h +++ b/py/qstrdefs.h @@ -213,6 +213,12 @@ Q(value) Q(write) Q(zip) +#if MICROPY_PY_BUILTINS_COMPILE +Q(compile) +Q(code) +Q(single) +#endif + Q(sep) Q(end) diff --git a/py/runtime.c b/py/runtime.c index f701e0a8d9..6cb5c7e197 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1189,6 +1189,13 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i nlr_raise(module_fun); } + // for compile only + if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) { + mp_globals_set(old_globals); + mp_locals_set(old_locals); + return module_fun; + } + // complied successfully, execute it nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h index a1e03be119..7844cc937e 100644 --- a/unix/mpconfigport.h +++ b/unix/mpconfigport.h @@ -47,6 +47,7 @@ #define MICROPY_PY_BUILTINS_STR_UNICODE (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW (1) #define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_COMPILE (1) #define MICROPY_PY_SYS_EXIT (1) #define MICROPY_PY_SYS_PLATFORM "linux" #define MICROPY_PY_SYS_MAXSIZE (1)