summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2025-01-19 23:30:59 +1100
committerDamien George <damien@micropython.org>2025-02-11 16:42:14 +1100
commit62e821ccb82fd8362a8198ad59ccb51b8a5c441e (patch)
tree36202713393c8bc4842171bc1cca13a4300b928b
parent372ecfef02eccc4e52a5d0abef068c7d25fc4315 (diff)
downloadmicropython-62e821ccb82fd8362a8198ad59ccb51b8a5c441e.tar.gz
micropython-62e821ccb82fd8362a8198ad59ccb51b8a5c441e.zip
py/objcode: Factor code object out into its own file.
The `mp_obj_code_t` and `mp_type_code` code object was defined internally in both `py/builtinevex.c` and `py/profile.c`, with completely different implementations (the former very minimal, the latter quite complete). This commit factors these implementations into a new, separate source file, and allows the code object to have four different modes, selected at compile-time: - MICROPY_PY_BUILTINS_CODE_NONE: code object not included in the build. - MICROPY_PY_BUILTINS_CODE_MINIMUM: very simple code object that just holds a reference to the function that it represents. This level is used when MICROPY_PY_BUILTINS_COMPILE is enabled. - MICROPY_PY_BUILTINS_CODE_BASIC: simple code object that holds a reference to the proto-function and its constants. - MICROPY_PY_BUILTINS_CODE_FULL: almost complete implementation of the code object. This level is used when MICROPY_PY_SYS_SETTRACE is enabled. Signed-off-by: Damien George <damien@micropython.org>
-rw-r--r--py/builtinevex.c51
-rw-r--r--py/compile.c2
-rw-r--r--py/compile.h5
-rw-r--r--py/mpconfig.h9
-rw-r--r--py/obj.h1
-rw-r--r--py/objcode.c175
-rw-r--r--py/objcode.h99
-rw-r--r--py/profile.c140
-rw-r--r--py/profile.h12
-rw-r--r--py/py.cmake1
-rw-r--r--py/py.mk1
-rw-r--r--py/runtime.c7
12 files changed, 337 insertions, 166 deletions
diff --git a/py/builtinevex.c b/py/builtinevex.c
index e25cbd4d08..74a4640492 100644
--- a/py/builtinevex.c
+++ b/py/builtinevex.c
@@ -26,6 +26,7 @@
#include <stdint.h>
+#include "py/objcode.h"
#include "py/objfun.h"
#include "py/compile.h"
#include "py/runtime.h"
@@ -33,17 +34,6 @@
#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 MP_DEFINE_CONST_OBJ_TYPE(
- mp_type_code,
- MP_QSTR_code,
- MP_TYPE_FLAG_NONE
- );
-
static mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) {
// save context
nlr_jump_callback_node_globals_locals_t ctx;
@@ -57,19 +47,28 @@ static mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj
// set exception handler to restore context if an exception is raised
nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback);
+ #if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC
+ mp_module_context_t *module_context = m_new_obj(mp_module_context_t);
+ module_context->module.base.type = &mp_type_module;
+ module_context->module.globals = globals;
+ module_context->constants = *mp_code_get_constants(self);
+ mp_obj_t module_fun = mp_make_function_from_proto_fun(mp_code_get_proto_fun(self), module_context, NULL);
+ #else
// The call to mp_parse_compile_execute() in mp_builtin_compile() below passes
// NULL for the globals, so repopulate that entry now with the correct globals.
+ mp_obj_t module_fun = self->module_fun;
if (mp_obj_is_type(self->module_fun, &mp_type_fun_bc)
#if MICROPY_EMIT_NATIVE
|| mp_obj_is_type(self->module_fun, &mp_type_fun_native)
#endif
) {
- mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun);
+ mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(module_fun);
((mp_module_context_t *)fun_bc->context)->module.globals = globals;
}
+ #endif
// execute code
- mp_obj_t ret = mp_call_function_0(self->module_fun);
+ mp_obj_t ret = mp_call_function_0(module_fun);
// deregister exception handler and restore context
nlr_pop_jump_callback(true);
@@ -108,9 +107,29 @@ static mp_obj_t mp_builtin_compile(size_t n_args, const mp_obj_t *args) {
mp_raise_ValueError(MP_ERROR_TEXT("bad compile mode"));
}
- mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code);
- code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL);
- return MP_OBJ_FROM_PTR(code);
+ #if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC
+
+ mp_parse_tree_t parse_tree = mp_parse(lex, parse_input_kind);
+ mp_module_context_t ctx;
+ ctx.module.globals = NULL;
+ mp_compiled_module_t cm;
+ cm.context = &ctx;
+ mp_compile_to_raw_code(&parse_tree, lex->source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT, &cm);
+
+ #if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_FULL
+ mp_module_context_t *ctx_ptr = m_new_obj(mp_module_context_t);
+ *ctx_ptr = ctx;
+ return mp_obj_new_code(ctx_ptr, cm.rc, true);
+ #else
+ return mp_obj_new_code(ctx.constants, cm.rc);
+ #endif
+
+ #else
+
+ mp_obj_t module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL);
+ return mp_obj_new_code(module_fun);
+
+ #endif
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile);
diff --git a/py/compile.c b/py/compile.c
index 60f06d7773..7a1151bcd6 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -3467,7 +3467,7 @@ static void scope_compute_things(scope_t *scope) {
}
}
-#if !MICROPY_PERSISTENT_CODE_SAVE
+#if !MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE
static
#endif
void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm) {
diff --git a/py/compile.h b/py/compile.h
index f9970a521d..64afc487d7 100644
--- a/py/compile.h
+++ b/py/compile.h
@@ -30,6 +30,9 @@
#include "py/parse.h"
#include "py/emitglue.h"
+// Whether mp_compile_to_raw_code is exposed as a public function.
+#define MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE (MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC || MICROPY_PERSISTENT_CODE_SAVE)
+
#if MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT
// set to `true` to allow top-level await expressions
extern bool mp_compile_allow_top_level_await;
@@ -40,7 +43,7 @@ extern bool mp_compile_allow_top_level_await;
// mp_globals_get() will be used for the context
mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl);
-#if MICROPY_PERSISTENT_CODE_SAVE
+#if MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE
// this has the same semantics as mp_compile
void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm);
#endif
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 64138a9ea7..76aff4681d 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -1129,6 +1129,15 @@ typedef double mp_float_t;
#define MICROPY_PY_BUILTINS_BYTEARRAY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
#endif
+// Whether to support code objects, and how many features they have
+#define MICROPY_PY_BUILTINS_CODE_NONE (0)
+#define MICROPY_PY_BUILTINS_CODE_MINIMUM (1)
+#define MICROPY_PY_BUILTINS_CODE_BASIC (2)
+#define MICROPY_PY_BUILTINS_CODE_FULL (3)
+#ifndef MICROPY_PY_BUILTINS_CODE
+#define MICROPY_PY_BUILTINS_CODE (MICROPY_PY_SYS_SETTRACE ? MICROPY_PY_BUILTINS_CODE_FULL : (MICROPY_PY_BUILTINS_COMPILE ? MICROPY_PY_BUILTINS_CODE_MINIMUM : MICROPY_PY_BUILTINS_CODE_NONE))
+#endif
+
// Whether to support dict.fromkeys() class method
#ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS
#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
diff --git a/py/obj.h b/py/obj.h
index bd31ef11d0..93d1e0ee34 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -840,6 +840,7 @@ extern const mp_obj_type_t mp_type_fun_bc;
extern const mp_obj_type_t mp_type_fun_native;
extern const mp_obj_type_t mp_type_fun_viper;
extern const mp_obj_type_t mp_type_fun_asm;
+extern const mp_obj_type_t mp_type_code;
extern const mp_obj_type_t mp_type_module;
extern const mp_obj_type_t mp_type_staticmethod;
extern const mp_obj_type_t mp_type_classmethod;
diff --git a/py/objcode.c b/py/objcode.c
new file mode 100644
index 0000000000..9b98a69679
--- /dev/null
+++ b/py/objcode.c
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "py/objcode.h"
+
+#if MICROPY_PY_BUILTINS_CODE == MICROPY_PY_BUILTINS_CODE_NONE
+
+// Code object not implemented at this configuration level.
+
+#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC
+
+MP_DEFINE_CONST_OBJ_TYPE(
+ mp_type_code,
+ MP_QSTR_code,
+ MP_TYPE_FLAG_NONE
+ );
+
+#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_FULL
+
+#include "py/profile.h"
+
+static void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
+ (void)kind;
+ mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in);
+ const mp_raw_code_t *rc = o->rc;
+ const mp_bytecode_prelude_t *prelude = &rc->prelude;
+ mp_printf(print,
+ "<code object %q at 0x%p, file \"%q\", line %d>",
+ MP_CODE_QSTR_MAP(o->context, prelude->qstr_block_name_idx),
+ o,
+ MP_CODE_QSTR_MAP(o->context, 0),
+ rc->line_of_definition
+ );
+}
+
+static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) {
+ mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL));
+
+ size_t const_no = 0;
+ for (size_t i = 0; i < rc->n_children; ++i) {
+ mp_obj_t code = mp_obj_new_code(context, rc->children[i], true);
+ consts->items[const_no++] = code;
+ }
+ consts->items[const_no++] = mp_const_none;
+
+ return consts;
+}
+
+static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) {
+ // const mp_bytecode_prelude_t *prelude = &rc->prelude;
+ uint start = 0;
+ uint stop = rc->fun_data_len - start;
+
+ uint last_lineno = mp_prof_bytecode_lineno(rc, start);
+ uint lasti = 0;
+
+ const uint buffer_chunk_size = (stop - start) >> 2; // heuristic magic
+ uint buffer_size = buffer_chunk_size;
+ byte *buffer = m_new(byte, buffer_size);
+ uint buffer_index = 0;
+
+ for (uint i = start; i < stop; ++i) {
+ uint lineno = mp_prof_bytecode_lineno(rc, i);
+ size_t line_diff = lineno - last_lineno;
+ if (line_diff > 0) {
+ uint instr_diff = (i - start) - lasti;
+
+ assert(instr_diff < 256);
+ assert(line_diff < 256);
+
+ if (buffer_index + 2 > buffer_size) {
+ buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size);
+ buffer_size = buffer_size + buffer_chunk_size;
+ }
+ last_lineno = lineno;
+ lasti = i - start;
+ buffer[buffer_index++] = instr_diff;
+ buffer[buffer_index++] = line_diff;
+ }
+ }
+
+ mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index);
+ m_del(byte, buffer, buffer_size);
+ return o;
+}
+
+static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+ if (dest[0] != MP_OBJ_NULL) {
+ // not load attribute
+ return;
+ }
+ mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in);
+ const mp_raw_code_t *rc = o->rc;
+ const mp_bytecode_prelude_t *prelude = &rc->prelude;
+ switch (attr) {
+ case MP_QSTR_co_code:
+ dest[0] = mp_obj_new_bytes(
+ (void *)prelude->opcodes,
+ rc->fun_data_len - (prelude->opcodes - (const byte *)rc->fun_data)
+ );
+ break;
+ case MP_QSTR_co_consts:
+ dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc));
+ break;
+ case MP_QSTR_co_filename:
+ dest[0] = MP_OBJ_NEW_QSTR(MP_CODE_QSTR_MAP(o->context, 0));
+ break;
+ case MP_QSTR_co_firstlineno:
+ dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0));
+ break;
+ case MP_QSTR_co_name:
+ dest[0] = MP_OBJ_NEW_QSTR(MP_CODE_QSTR_MAP(o->context, prelude->qstr_block_name_idx));
+ break;
+ case MP_QSTR_co_names:
+ dest[0] = MP_OBJ_FROM_PTR(o->dict_locals);
+ break;
+ case MP_QSTR_co_lnotab:
+ if (!o->lnotab) {
+ o->lnotab = raw_code_lnotab(rc);
+ }
+ dest[0] = o->lnotab;
+ break;
+ }
+}
+
+MP_DEFINE_CONST_OBJ_TYPE(
+ mp_type_code,
+ MP_QSTR_code,
+ MP_TYPE_FLAG_NONE,
+ print, code_print,
+ attr, code_attr
+ );
+
+mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc, bool result_required) {
+ mp_obj_code_t *o;
+ if (result_required) {
+ o = m_new_obj(mp_obj_code_t);
+ } else {
+ o = m_new_obj_maybe(mp_obj_code_t);
+ if (o == NULL) {
+ return MP_OBJ_NULL;
+ }
+ }
+ o->base.type = &mp_type_code;
+ o->context = context;
+ o->rc = rc;
+ o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly?
+ o->lnotab = MP_OBJ_NULL;
+ return MP_OBJ_FROM_PTR(o);
+}
+
+#endif
diff --git a/py/objcode.h b/py/objcode.h
new file mode 100644
index 0000000000..8db9a34b6e
--- /dev/null
+++ b/py/objcode.h
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_OBJCODE_H
+#define MICROPY_INCLUDED_PY_OBJCODE_H
+
+#include "py/bc.h"
+
+#if MICROPY_PY_BUILTINS_CODE == MICROPY_PY_BUILTINS_CODE_NONE
+
+// Code object not implemented at this configuration level.
+
+#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_MINIMUM
+
+typedef struct _mp_obj_code_t {
+ mp_obj_base_t base;
+ mp_obj_t module_fun;
+} mp_obj_code_t;
+
+static inline mp_obj_t mp_obj_new_code(mp_obj_t module_fun) {
+ mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code);
+ code->module_fun = module_fun;
+ return MP_OBJ_FROM_PTR(code);
+}
+
+#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC
+
+typedef struct _mp_obj_code_t {
+ mp_obj_base_t base;
+ mp_module_constants_t constants;
+ const void *proto_fun;
+} mp_obj_code_t;
+
+static inline mp_obj_t mp_obj_new_code(const mp_module_constants_t constants, const void *proto_fun) {
+ mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code);
+ code->constants = constants;
+ code->proto_fun = proto_fun;
+ return MP_OBJ_FROM_PTR(code);
+}
+
+static inline const mp_module_constants_t *mp_code_get_constants(mp_obj_code_t *self) {
+ return &self->constants;
+}
+
+static inline const void *mp_code_get_proto_fun(mp_obj_code_t *self) {
+ return self->proto_fun;
+}
+
+#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_FULL
+
+#include "py/emitglue.h"
+
+#define MP_CODE_QSTR_MAP(context, idx) (context->constants.qstr_table[idx])
+
+typedef struct _mp_obj_code_t {
+ // TODO this was 4 words
+ mp_obj_base_t base;
+ const mp_module_context_t *context;
+ const mp_raw_code_t *rc;
+ mp_obj_dict_t *dict_locals;
+ mp_obj_t lnotab;
+} mp_obj_code_t;
+
+mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc, bool result_required);
+
+static inline const mp_module_constants_t *mp_code_get_constants(mp_obj_code_t *self) {
+ return &self->context->constants;
+}
+
+static inline const void *mp_code_get_proto_fun(mp_obj_code_t *self) {
+ // A mp_raw_code_t is always a proto_fun (but not the other way around).
+ return self->rc;
+}
+
+#endif
+
+#endif // MICROPY_INCLUDED_PY_OBJCODE_H
diff --git a/py/profile.c b/py/profile.c
index 92f414ace7..397d0291f9 100644
--- a/py/profile.c
+++ b/py/profile.c
@@ -38,9 +38,8 @@
#endif
#define prof_trace_cb MP_STATE_THREAD(prof_trace_callback)
-#define QSTR_MAP(context, idx) (context->constants.qstr_table[idx])
-static uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) {
+uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) {
const mp_bytecode_prelude_t *prelude = &rc->prelude;
return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc);
}
@@ -69,137 +68,6 @@ void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelud
}
/******************************************************************************/
-// code object
-
-static void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
- (void)kind;
- mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in);
- const mp_raw_code_t *rc = o->rc;
- const mp_bytecode_prelude_t *prelude = &rc->prelude;
- mp_printf(print,
- "<code object %q at 0x%p, file \"%q\", line %d>",
- QSTR_MAP(o->context, prelude->qstr_block_name_idx),
- o,
- QSTR_MAP(o->context, 0),
- rc->line_of_definition
- );
-}
-
-static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) {
- mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL));
-
- size_t const_no = 0;
- for (size_t i = 0; i < rc->n_children; ++i) {
- mp_obj_t code = mp_obj_new_code(context, rc->children[i]);
- if (code == MP_OBJ_NULL) {
- m_malloc_fail(sizeof(mp_obj_code_t));
- }
- consts->items[const_no++] = code;
- }
- consts->items[const_no++] = mp_const_none;
-
- return consts;
-}
-
-static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) {
- // const mp_bytecode_prelude_t *prelude = &rc->prelude;
- uint start = 0;
- uint stop = rc->fun_data_len - start;
-
- uint last_lineno = mp_prof_bytecode_lineno(rc, start);
- uint lasti = 0;
-
- const uint buffer_chunk_size = (stop - start) >> 2; // heuristic magic
- uint buffer_size = buffer_chunk_size;
- byte *buffer = m_new(byte, buffer_size);
- uint buffer_index = 0;
-
- for (uint i = start; i < stop; ++i) {
- uint lineno = mp_prof_bytecode_lineno(rc, i);
- size_t line_diff = lineno - last_lineno;
- if (line_diff > 0) {
- uint instr_diff = (i - start) - lasti;
-
- assert(instr_diff < 256);
- assert(line_diff < 256);
-
- if (buffer_index + 2 > buffer_size) {
- buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size);
- buffer_size = buffer_size + buffer_chunk_size;
- }
- last_lineno = lineno;
- lasti = i - start;
- buffer[buffer_index++] = instr_diff;
- buffer[buffer_index++] = line_diff;
- }
- }
-
- mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index);
- m_del(byte, buffer, buffer_size);
- return o;
-}
-
-static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
- if (dest[0] != MP_OBJ_NULL) {
- // not load attribute
- return;
- }
- mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in);
- const mp_raw_code_t *rc = o->rc;
- const mp_bytecode_prelude_t *prelude = &rc->prelude;
- switch (attr) {
- case MP_QSTR_co_code:
- dest[0] = mp_obj_new_bytes(
- (void *)prelude->opcodes,
- rc->fun_data_len - (prelude->opcodes - (const byte *)rc->fun_data)
- );
- break;
- case MP_QSTR_co_consts:
- dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc));
- break;
- case MP_QSTR_co_filename:
- dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, 0));
- break;
- case MP_QSTR_co_firstlineno:
- dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0));
- break;
- case MP_QSTR_co_name:
- dest[0] = MP_OBJ_NEW_QSTR(QSTR_MAP(o->context, prelude->qstr_block_name_idx));
- break;
- case MP_QSTR_co_names:
- dest[0] = MP_OBJ_FROM_PTR(o->dict_locals);
- break;
- case MP_QSTR_co_lnotab:
- if (!o->lnotab) {
- o->lnotab = raw_code_lnotab(rc);
- }
- dest[0] = o->lnotab;
- break;
- }
-}
-
-MP_DEFINE_CONST_OBJ_TYPE(
- mp_type_settrace_codeobj,
- MP_QSTR_code,
- MP_TYPE_FLAG_NONE,
- print, code_print,
- attr, code_attr
- );
-
-mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc) {
- mp_obj_code_t *o = m_new_obj_maybe(mp_obj_code_t);
- if (o == NULL) {
- return MP_OBJ_NULL;
- }
- o->base.type = &mp_type_settrace_codeobj;
- o->context = context;
- o->rc = rc;
- o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly?
- o->lnotab = MP_OBJ_NULL;
- return MP_OBJ_FROM_PTR(o);
-}
-
-/******************************************************************************/
// frame object
static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
@@ -211,9 +79,9 @@ static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t
mp_printf(print,
"<frame at 0x%p, file '%q', line %d, code %q>",
frame,
- QSTR_MAP(code->context, 0),
+ MP_CODE_QSTR_MAP(code->context, 0),
frame->lineno,
- QSTR_MAP(code->context, prelude->qstr_block_name_idx)
+ MP_CODE_QSTR_MAP(code->context, prelude->qstr_block_name_idx)
);
}
@@ -265,7 +133,7 @@ mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state) {
return MP_OBJ_NULL;
}
- mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->context, code_state->fun_bc->rc));
+ mp_obj_code_t *code = o->code = MP_OBJ_TO_PTR(mp_obj_new_code(code_state->fun_bc->context, code_state->fun_bc->rc, false));
if (code == NULL) {
return MP_OBJ_NULL;
}
diff --git a/py/profile.h b/py/profile.h
index 7f3f914034..db72b9f076 100644
--- a/py/profile.h
+++ b/py/profile.h
@@ -28,20 +28,12 @@
#define MICROPY_INCLUDED_PY_PROFILING_H
#include "py/emitglue.h"
+#include "py/objcode.h"
#if MICROPY_PY_SYS_SETTRACE
#define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing)
-typedef struct _mp_obj_code_t {
- // TODO this was 4 words
- mp_obj_base_t base;
- const mp_module_context_t *context;
- const mp_raw_code_t *rc;
- mp_obj_dict_t *dict_locals;
- mp_obj_t lnotab;
-} mp_obj_code_t;
-
typedef struct _mp_obj_frame_t {
mp_obj_base_t base;
const mp_code_state_t *code_state;
@@ -53,9 +45,9 @@ typedef struct _mp_obj_frame_t {
bool trace_opcodes;
} mp_obj_frame_t;
+uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc);
void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude);
-mp_obj_t mp_obj_new_code(const mp_module_context_t *mc, const mp_raw_code_t *rc);
mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state);
// This is the implementation for the sys.settrace
diff --git a/py/py.cmake b/py/py.cmake
index 0fee74ddcb..1c81ed4c58 100644
--- a/py/py.cmake
+++ b/py/py.cmake
@@ -72,6 +72,7 @@ set(MICROPY_SOURCE_PY
${MICROPY_PY_DIR}/objattrtuple.c
${MICROPY_PY_DIR}/objbool.c
${MICROPY_PY_DIR}/objboundmeth.c
+ ${MICROPY_PY_DIR}/objcode.c
${MICROPY_PY_DIR}/objcell.c
${MICROPY_PY_DIR}/objclosure.c
${MICROPY_PY_DIR}/objcomplex.c
diff --git a/py/py.mk b/py/py.mk
index c0b7e1ac8b..e352d89792 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -149,6 +149,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\
objboundmeth.o \
objcell.o \
objclosure.o \
+ objcode.o \
objcomplex.o \
objdeque.o \
objdict.o \
diff --git a/py/runtime.c b/py/runtime.c
index deb55bf283..bf26921f9a 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1620,10 +1620,13 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT);
mp_obj_t ret;
- if (MICROPY_PY_BUILTINS_COMPILE && globals == NULL) {
+ #if MICROPY_PY_BUILTINS_COMPILE && MICROPY_PY_BUILTINS_CODE == MICROPY_PY_BUILTINS_CODE_MINIMUM
+ if (globals == NULL) {
// for compile only, return value is the module function
ret = module_fun;
- } else {
+ } else
+ #endif
+ {
// execute module function and get return value
ret = mp_call_function_0(module_fun);
}