From aa3a4362e3bde17e8eebb1f9790770a9b0979f99 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Wed, 22 Jun 2022 10:36:41 +1000 Subject: [PATCH] py/objfun: Add function.__code__ attribute. This is used to provide introspection of attributes such as function name or source file & line. Signed-off-by: Andrew Leech --- py/emitbc.c | 2 +- py/emitglue.c | 6 +++--- py/emitglue.h | 4 +--- py/objfun.c | 8 ++++++++ py/objfun.h | 2 +- py/profile.c | 23 ++++++++++++++++++++++- py/profile.h | 15 ++++++++------- 7 files changed, 44 insertions(+), 16 deletions(-) diff --git a/py/emitbc.c b/py/emitbc.c index a07657408fab..7eec4a6ecf47 100644 --- a/py/emitbc.c +++ b/py/emitbc.c @@ -217,7 +217,7 @@ STATIC void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp STATIC void emit_write_bytecode_byte_child(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) { emit_write_bytecode_byte_const(emit, stack_adj, b, mp_emit_common_alloc_const_child(emit->emit_common, rc)); - #if MICROPY_PY_SYS_SETTRACE + #if MICROPY_PY_SYS_SETTRACE || MICROPY_PY_FUNCTION_ATTRS rc->line_of_definition = emit->last_source_line; #endif } diff --git a/py/emitglue.c b/py/emitglue.c index 6ec6d6b885d4..64d5ad611293 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -54,7 +54,7 @@ mp_uint_t mp_verbose_flag = 0; mp_raw_code_t *mp_emit_glue_new_raw_code(void) { mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1); rc->kind = MP_CODE_RESERVED; - #if MICROPY_PY_SYS_SETTRACE + #if MICROPY_PY_SYS_SETTRACE || MICROPY_PY_FUNCTION_ATTRS rc->line_of_definition = 0; #endif return rc; @@ -82,7 +82,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, rc->n_children = n_children; #endif - #if MICROPY_PY_SYS_SETTRACE + #if MICROPY_PY_SYS_SETTRACE || MICROPY_PY_FUNCTION_ATTRS mp_bytecode_prelude_t *prelude = &rc->prelude; mp_prof_extract_prelude(code, prelude); #endif @@ -207,7 +207,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module ((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap; } - #if MICROPY_PY_SYS_SETTRACE + #if MICROPY_PY_SYS_SETTRACE || MICROPY_PY_FUNCTION_ATTRS mp_obj_fun_bc_t *self_fun = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(fun); self_fun->rc = rc; #endif diff --git a/py/emitglue.h b/py/emitglue.h index 4ddf74011fa7..99332281e72f 100644 --- a/py/emitglue.h +++ b/py/emitglue.h @@ -61,16 +61,14 @@ typedef struct _mp_raw_code_t { size_t fun_data_len; // so mp_raw_code_save and mp_bytecode_print work #endif struct _mp_raw_code_t **children; - #if MICROPY_PERSISTENT_CODE_SAVE + #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PY_FUNCTION_ATTRS size_t n_children; - #if MICROPY_PY_SYS_SETTRACE mp_bytecode_prelude_t prelude; // line_of_definition is a Python source line where the raw_code was // created e.g. MP_BC_MAKE_FUNCTION. This is different from lineno info // stored in prelude, which provides line number for first statement of // a function. Required to properly implement "call" trace event. mp_uint_t line_of_definition; - #endif #if MICROPY_EMIT_MACHINE_CODE uint16_t prelude_offset; #endif diff --git a/py/objfun.c b/py/objfun.c index e2136968b621..13dd8a68f17f 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -33,6 +33,7 @@ #include "py/runtime.h" #include "py/bc.h" #include "py/stackctrl.h" +#include "py/profile.h" #if MICROPY_DEBUG_VERBOSE // print debugging info #define DEBUG_PRINT (1) @@ -344,6 +345,13 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); dest[0] = MP_OBJ_FROM_PTR(self->context->module.globals); } + if (attr == MP_QSTR___code__) { + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_code_t *code = MP_OBJ_TO_PTR(mp_obj_new_code(self->context, self->rc)); + if (code != NULL) { + dest[0] = MP_OBJ_FROM_PTR(code); + } + } } #endif diff --git a/py/objfun.h b/py/objfun.h index 9de15b884155..1418163df085 100644 --- a/py/objfun.h +++ b/py/objfun.h @@ -34,7 +34,7 @@ typedef struct _mp_obj_fun_bc_t { const mp_module_context_t *context; // context within which this function was defined struct _mp_raw_code_t *const *child_table; // table of children const byte *bytecode; // bytecode for the function - #if MICROPY_PY_SYS_SETTRACE + #if MICROPY_PY_SYS_SETTRACE || MICROPY_PY_FUNCTION_ATTRS const struct _mp_raw_code_t *rc; #endif // the following extra_args array is allocated space to take (in order): diff --git a/py/profile.c b/py/profile.c index c0641e49c98e..5a3493b8b54c 100644 --- a/py/profile.c +++ b/py/profile.c @@ -29,6 +29,14 @@ #include "py/gc.h" #include "py/objfun.h" +#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PY_FUNCTION_ATTRS + +#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE +#define QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) +#else +#define QSTR_MAP(context, idx) ((idx)? idx: context->constants.source_file) +#endif + #if MICROPY_PY_SYS_SETTRACE #if !MICROPY_PERSISTENT_CODE_SAVE @@ -38,7 +46,8 @@ #endif #define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) -#define QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) + +#endif // MICROPY_PY_SYS_SETTRACE STATIC uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) { const mp_bytecode_prelude_t *prelude = &rc->prelude; @@ -101,7 +110,10 @@ STATIC mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_ return consts; } +#if MICROPY_PY_SYS_SETTRACE STATIC mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { + // Returns a string encoding the mapping from bytecode offsets to line numbers. + // const mp_bytecode_prelude_t *prelude = &rc->prelude; uint start = 0; uint stop = rc->fun_data_len - start; @@ -138,6 +150,7 @@ STATIC mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { m_del(byte, buffer, buffer_size); return o; } +#endif // MICROPY_PY_SYS_SETTRACE STATIC void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] != MP_OBJ_NULL) { @@ -148,12 +161,14 @@ STATIC void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { const mp_raw_code_t *rc = o->rc; const mp_bytecode_prelude_t *prelude = &rc->prelude; switch (attr) { + #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS 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; + #endif case MP_QSTR_co_consts: dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc)); break; @@ -199,12 +214,14 @@ STATIC void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } break; } + #if MICROPY_PY_SYS_SETTRACE case MP_QSTR_co_lnotab: if (!o->lnotab) { o->lnotab = raw_code_lnotab(rc); } dest[0] = o->lnotab; break; + #endif } } @@ -229,6 +246,8 @@ mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t return MP_OBJ_FROM_PTR(o); } +#if MICROPY_PY_SYS_SETTRACE + /******************************************************************************/ // frame object @@ -1005,3 +1024,5 @@ void mp_prof_print_instr(const byte *ip, mp_code_state_t *code_state) { #endif // MICROPY_PROF_INSTR_DEBUG_PRINT_ENABLE #endif // MICROPY_PY_SYS_SETTRACE + +#endif // MICROPY_PERSISTENT_CODE_SAVE || MICROPY_PY_FUNCTION_ATTRS diff --git a/py/profile.h b/py/profile.h index 7f3f91403462..e17bb8e7990f 100644 --- a/py/profile.h +++ b/py/profile.h @@ -29,10 +29,6 @@ #include "py/emitglue.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; @@ -42,6 +38,14 @@ typedef struct _mp_obj_code_t { mp_obj_t lnotab; } mp_obj_code_t; +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); + +#if MICROPY_PY_SYS_SETTRACE + +#define mp_prof_is_executing MP_STATE_THREAD(prof_callback_is_executing) + typedef struct _mp_obj_frame_t { mp_obj_base_t base; const mp_code_state_t *code_state; @@ -53,9 +57,6 @@ typedef struct _mp_obj_frame_t { bool trace_opcodes; } mp_obj_frame_t; -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