mirror of
https://github.com/micropython/micropython.git
synced 2025-07-21 04:51: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 "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);
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
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_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;
|
||||
|
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
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -68,137 +67,6 @@ void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelud
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
12
py/profile.h
12
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
|
||||
|
@@ -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
|
||||
|
1
py/py.mk
1
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 \
|
||||
|
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user