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:
Damien George
2025-01-19 23:30:59 +11:00
parent 372ecfef02
commit 62e821ccb8
12 changed files with 337 additions and 166 deletions

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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

View File

@@ -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)

View File

@@ -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
View 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
View 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

View File

@@ -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;
} }

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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);
} }