mirror of
https://github.com/micropython/micropython.git
synced 2025-07-21 21:11:12 +02:00
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>
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "py/objcode.h"
|
||||||
#include "py/objfun.h"
|
#include "py/objfun.h"
|
||||||
#include "py/compile.h"
|
#include "py/compile.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
@@ -33,17 +34,6 @@
|
|||||||
|
|
||||||
#if MICROPY_PY_BUILTINS_COMPILE
|
#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) {
|
static mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) {
|
||||||
// save context
|
// save context
|
||||||
nlr_jump_callback_node_globals_locals_t ctx;
|
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
|
// 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);
|
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
|
// 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.
|
// 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 (mp_obj_is_type(self->module_fun, &mp_type_fun_bc)
|
||||||
#if MICROPY_EMIT_NATIVE
|
#if MICROPY_EMIT_NATIVE
|
||||||
|| mp_obj_is_type(self->module_fun, &mp_type_fun_native)
|
|| mp_obj_is_type(self->module_fun, &mp_type_fun_native)
|
||||||
#endif
|
#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;
|
((mp_module_context_t *)fun_bc->context)->module.globals = globals;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// execute code
|
// 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
|
// deregister exception handler and restore context
|
||||||
nlr_pop_jump_callback(true);
|
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_raise_ValueError(MP_ERROR_TEXT("bad compile mode"));
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code);
|
#if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC
|
||||||
code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL);
|
|
||||||
return MP_OBJ_FROM_PTR(code);
|
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);
|
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile);
|
||||||
|
|
||||||
|
@@ -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
|
static
|
||||||
#endif
|
#endif
|
||||||
void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm) {
|
void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm) {
|
||||||
|
@@ -30,6 +30,9 @@
|
|||||||
#include "py/parse.h"
|
#include "py/parse.h"
|
||||||
#include "py/emitglue.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
|
#if MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT
|
||||||
// set to `true` to allow top-level await expressions
|
// set to `true` to allow top-level await expressions
|
||||||
extern bool mp_compile_allow_top_level_await;
|
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_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);
|
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
|
// 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);
|
void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -1129,6 +1129,15 @@ typedef double mp_float_t;
|
|||||||
#define MICROPY_PY_BUILTINS_BYTEARRAY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
|
#define MICROPY_PY_BUILTINS_BYTEARRAY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
|
||||||
#endif
|
#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
|
// Whether to support dict.fromkeys() class method
|
||||||
#ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS
|
#ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS
|
||||||
#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
|
#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
|
||||||
|
1
py/obj.h
1
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_native;
|
||||||
extern const mp_obj_type_t mp_type_fun_viper;
|
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_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_module;
|
||||||
extern const mp_obj_type_t mp_type_staticmethod;
|
extern const mp_obj_type_t mp_type_staticmethod;
|
||||||
extern const mp_obj_type_t mp_type_classmethod;
|
extern const mp_obj_type_t mp_type_classmethod;
|
||||||
|
175
py/objcode.c
Normal file
175
py/objcode.c
Normal file
@@ -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
|
99
py/objcode.h
Normal file
99
py/objcode.h
Normal file
@@ -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
|
140
py/profile.c
140
py/profile.c
@@ -38,9 +38,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define prof_trace_cb MP_STATE_THREAD(prof_trace_callback)
|
#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;
|
const mp_bytecode_prelude_t *prelude = &rc->prelude;
|
||||||
return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc);
|
return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc);
|
||||||
}
|
}
|
||||||
@@ -68,137 +67,6 @@ void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelud
|
|||||||
prelude->line_info = ip;
|
prelude->line_info = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
// 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
|
// frame object
|
||||||
|
|
||||||
@@ -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,
|
mp_printf(print,
|
||||||
"<frame at 0x%p, file '%q', line %d, code %q>",
|
"<frame at 0x%p, file '%q', line %d, code %q>",
|
||||||
frame,
|
frame,
|
||||||
QSTR_MAP(code->context, 0),
|
MP_CODE_QSTR_MAP(code->context, 0),
|
||||||
frame->lineno,
|
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;
|
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) {
|
if (code == NULL) {
|
||||||
return MP_OBJ_NULL;
|
return MP_OBJ_NULL;
|
||||||
}
|
}
|
||||||
|
12
py/profile.h
12
py/profile.h
@@ -28,20 +28,12 @@
|
|||||||
#define MICROPY_INCLUDED_PY_PROFILING_H
|
#define MICROPY_INCLUDED_PY_PROFILING_H
|
||||||
|
|
||||||
#include "py/emitglue.h"
|
#include "py/emitglue.h"
|
||||||
|
#include "py/objcode.h"
|
||||||
|
|
||||||
#if MICROPY_PY_SYS_SETTRACE
|
#if MICROPY_PY_SYS_SETTRACE
|
||||||
|
|
||||||
#define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing)
|
#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 {
|
typedef struct _mp_obj_frame_t {
|
||||||
mp_obj_base_t base;
|
mp_obj_base_t base;
|
||||||
const mp_code_state_t *code_state;
|
const mp_code_state_t *code_state;
|
||||||
@@ -53,9 +45,9 @@ typedef struct _mp_obj_frame_t {
|
|||||||
bool trace_opcodes;
|
bool trace_opcodes;
|
||||||
} mp_obj_frame_t;
|
} 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);
|
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);
|
mp_obj_t mp_obj_new_frame(const mp_code_state_t *code_state);
|
||||||
|
|
||||||
// This is the implementation for the sys.settrace
|
// This is the implementation for the sys.settrace
|
||||||
|
@@ -72,6 +72,7 @@ set(MICROPY_SOURCE_PY
|
|||||||
${MICROPY_PY_DIR}/objattrtuple.c
|
${MICROPY_PY_DIR}/objattrtuple.c
|
||||||
${MICROPY_PY_DIR}/objbool.c
|
${MICROPY_PY_DIR}/objbool.c
|
||||||
${MICROPY_PY_DIR}/objboundmeth.c
|
${MICROPY_PY_DIR}/objboundmeth.c
|
||||||
|
${MICROPY_PY_DIR}/objcode.c
|
||||||
${MICROPY_PY_DIR}/objcell.c
|
${MICROPY_PY_DIR}/objcell.c
|
||||||
${MICROPY_PY_DIR}/objclosure.c
|
${MICROPY_PY_DIR}/objclosure.c
|
||||||
${MICROPY_PY_DIR}/objcomplex.c
|
${MICROPY_PY_DIR}/objcomplex.c
|
||||||
|
1
py/py.mk
1
py/py.mk
@@ -149,6 +149,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\
|
|||||||
objboundmeth.o \
|
objboundmeth.o \
|
||||||
objcell.o \
|
objcell.o \
|
||||||
objclosure.o \
|
objclosure.o \
|
||||||
|
objcode.o \
|
||||||
objcomplex.o \
|
objcomplex.o \
|
||||||
objdeque.o \
|
objdeque.o \
|
||||||
objdict.o \
|
objdict.o \
|
||||||
|
@@ -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 module_fun = mp_compile(&parse_tree, source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT);
|
||||||
|
|
||||||
mp_obj_t ret;
|
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
|
// for compile only, return value is the module function
|
||||||
ret = module_fun;
|
ret = module_fun;
|
||||||
} else {
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
// execute module function and get return value
|
// execute module function and get return value
|
||||||
ret = mp_call_function_0(module_fun);
|
ret = mp_call_function_0(module_fun);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user