8000 py/objcode: Factor code object out into its own file. · sparkfun/micropython@62e821c · GitHub
[go: up one dir, main page]

Skip to content

Commit 62e821c

Browse files
committed
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>
1 parent 372ecfe commit 62e821c

File tree

12 files changed

+337
-166
lines changed

12 files changed

+337
-166
lines changed

py/builtinevex.c

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,14 @@
2626

2727
#include <stdint.h>
2828

29+
#include "py/objcode.h"
2930
#include "py/objfun.h"
3031
#include "py/compile.h"
3132
#include "py/runtime.h"
3233
#include "py/builtin.h"
3334

3435
#if MICROPY_PY_BUILTINS_COMPILE
3536

36-
typedef struct _mp_obj_code_t {
37-
mp_obj_base_t base;
38-
mp_obj_t module_fun;
39-
} mp_obj_code_t;
40-
41-
static MP_DEFINE_CONST_OBJ_TYPE(
42-
mp_type_code,
43-
MP_QSTR_code,
44-
MP_TYPE_FLAG_NONE
45-
);
46-
4737
static mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) {
4838
// save context
4939
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
5747
// set exception handler to restore context if an exception is raised
5848
nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback);
5949

50+
#if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC
51+
mp_module_context_t *module_context = m_new_obj(mp_module_context_t);
52+
module_context->module.base.type = &mp_type_module;
53+
module_context->module.globals = globals;
54+
module_context->constants = *mp_code_get_constants(self);
55+
mp_obj_t module_fun = mp_make_function_from_proto_fun(mp_code_get_proto_fun(self), module_context, NULL);
56+
#else
6057
// The call to mp_parse_compile_execute() in mp_builtin_compile() below passes
6158
// NULL for the globals, so repopulate that entry now with the correct globals.
59+
mp_obj_t module_fun = self->module_fun;
6260
if (mp_obj_is_type(self->module_fun, &mp_type_fun_bc)
6361
#if MICROPY_EMIT_NATIVE
6462
|| mp_obj_is_type(self->module_fun, &mp_type_fun_native)
6563
#endif
6664
) {
67-
mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun);
65+
mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(module_fun);
6866
((mp_module_context_t *)fun_bc->context)->module.globals = globals;
6967
}
68+
#endif
7069

7170
// execute code
72-
mp_obj_t ret = mp_call_function_0(self->module_fun);
71+
mp_obj_t ret = mp_call_function_0(module_fun);
7372

7473
// deregister exception handler and restore context
7574
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) {
108107
mp_raise_ValueError(MP_ERROR_TEXT("bad compile mode"));
109108
}
110109

111-
mp_obj_code_t *code = mp_obj_malloc(mp_obj_code_t, &mp_type_code);
112-
code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL);
113-
return MP_OBJ_FROM_PTR(code);
110+
#if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC
111+
112+
mp_parse_tree_t parse_tree = mp_parse(lex, parse_input_kind);
113+
mp_module_context_t ctx;
114+
ctx.module.globals = NULL;
115+
mp_compiled_module_t cm;
116+
cm.context = &ctx;
117+
mp_compile_to_raw_code(&parse_tree, lex->source_name, parse_input_kind == MP_PARSE_SINGLE_INPUT, &cm);
118+
119+
#if MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_FULL
120+
mp_module_context_t *ctx_ptr = m_new_obj(mp_module_context_t);
121+
*ctx_ptr = ctx;
122+
return mp_obj_new_code(ctx_ptr, cm.rc, true);
123+
#else
124+
return mp_obj_new_code(ctx.constants, cm.rc);
125+
#endif
126+
127+
#else
128+
129+
mp_obj_t module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL);
130+
return mp_obj_new_code(module_fun);
131+
132+
#endif
114133
}
115134
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile);
116135

py/compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3467,7 +3467,7 @@ static void scope_compute_things(scope_t *scope) {
34673467
}
34683468
}
34693469

3470-
#if !MICROPY_PERSISTENT_CODE_SAVE
3470+
#if !MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE
34713471
static
34723472
#endif
34733473
void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm) {

py/compile.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
#include "py/parse.h"
3131
#include "py/emitglue.h"
3232

33+
// Whether mp_compile_to_raw_code is exposed as a public function.
34+
#define MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE (MICROPY_PY_BUILTINS_CODE >= MICROPY_PY_BUILTINS_CODE_BASIC || MICROPY_PERSISTENT_CODE_SAVE)
35+
3336
#if MICROPY_COMP_ALLOW_TOP_LEVEL_AWAIT
3437
// set to `true` to allow top-level await expressions
3538
extern bool mp_compile_allow_top_level_await;
@@ -40,7 +43,7 @@ extern bool mp_compile_allow_top_level_await;
4043
// mp_globals_get() will be used for the context
4144
mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl);
4245

43-
#if MICROPY_PERSISTENT_CODE_SAVE
46+
#if MICROPY_EXPOSE_MP_COMPILE_TO_RAW_CODE
4447
// this has the same semantics as mp_compile
4548
void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_compiled_module_t *cm);
4649
#endif

py/mpconfig.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,15 @@ typedef double mp_float_t;
11291129
#define MICROPY_PY_BUILTINS_BYTEARRAY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
11301130
#endif
11311131

1132+
// Whether to support code objects, and how many features they have
1133+
#define MICROPY_PY_BUILTINS_CODE_NONE (0)
1134+
#define MICROPY_PY_BUILTINS_CODE_MINIMUM (1)
1135+
#define MICROPY_PY_BUILTINS_CODE_BASIC (2)
1136+
#define MICROPY_PY_BUILTINS_CODE_FULL (3)
1137+
#ifndef MICROPY_PY_BUILTINS_CODE
1138+
#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))
1139+
#endif
1140+
11321141
// Whether to support dict.fromkeys() class method
11331142
#ifndef MICROPY_PY_BUILTINS_DICT_FROMKEYS
11341143
#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)

py/obj.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,7 @@ extern const mp_obj_type_t mp_type_fun_bc;
840840
extern const mp_obj_type_t mp_type_fun_native;
841841
extern const mp_obj_type_t mp_type_fun_viper;
842842
extern const mp_obj_type_t mp_type_fun_asm;
843+
extern const mp_obj_type_t mp_type_code;
843844
extern const mp_obj_type_t mp_type_module;
844845
extern const mp_obj_type_t mp_type_staticmethod;
845846
extern const mp_obj_type_t mp_type_classmethod;

py/objcode.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2025 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "py/objcode.h"
28+
29+
#if MICROPY_PY_BUILTINS_CODE == MICROPY_PY_BUILTINS_CODE_NONE
30+
31+
// Code object not implemented at this configuration level.
32+
33+
#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC
34+
35+
MP_DEFINE_CONST_OBJ_TYPE(
36+
mp_type_code,
37+
MP_QSTR_code,
38+
MP_TYPE_FLAG_NONE
39+
);
40+
41+
#elif MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_FULL
42+
43+
#include "py/profile.h"
44+
45+
static void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
46+
(void)kind;
47+
mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in);
48+
const mp_raw_code_t *rc = o->rc;
49+
const mp_bytecode_prelude_t *prelude = &rc->prelude;
50+
mp_printf(print,
51+
"<code object %q at 0x%p, file \"%q\", line %d>",
52+
MP_CODE_QSTR_MAP(o->context, prelude->qstr_block_name_idx),
53+
o,
54+
MP_CODE_QSTR_MAP(o->context, 0),
55+
rc->line_of_definition
56+
);
57+
}
58+
59+
static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) {
60+
mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL));
61+
62+
size_t const_no = 0;
63+
for (size_t i = 0; i < rc->n_children; ++i) {
64+
mp_obj_t code = mp_obj_new_code(context, rc->children[i], true);
65+
consts->items[const_no++] = code;
66+
}
67+
consts->items[const_no++] = mp_const_none;
68+
69+
return consts;
70+
}
71+
72+
static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) {
73+
// const mp_bytecode_prelude_t *prelude = &rc->prelude;
74+
uint start = 0;
75+
uint stop = rc->fun_data_len - start;
76+
77+
uint last_lineno = mp_prof_bytecode_lineno(rc, start);
78+
uint lasti = 0;
79+
80+
const uint buffer_chunk_size = (stop - start) >> 2; // heuristic magic
81+
uint buffer_size = buffer_chunk_size;
82+
byte *buffer = m_new(byte, buffer_size);
83+
uint buffer_index = 0;
84+
85+
for (uint i = start; i < stop; ++i) {
86+
uint lineno = mp_prof_bytecode_lineno(rc, i);
87+
size_t line_diff = lineno - last_lineno;
88+
if (line_diff > 0) {
89+
uint instr_diff = (i - start) - lasti;
90+
91+
assert(instr_diff < 256);
92+
assert(line_diff < 256);
93+
94+
if (buffer_index + 2 > buffer_size) {
95+
buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size);
96+
buffer_size = buffer_size + buffer_chunk_size;
97+
}
98+
last_lineno = lineno;
99+
lasti = i - start;
100+
buffer[buffer_index++] = instr_diff;
101+
buffer[buffer_index++] = line_diff;
102+
}
103+
}
104+
105+
mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index);
106+
m_del(byte, buffer, buffer_size);
107+
return o;
108+
}
109+
110+
static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
111+
if (dest[0] != MP_OBJ_NULL) {
112+
// not load attribute
113+
return;
114+
}
115+
mp_obj_code_t *o = MP_OBJ_TO_PTR(self_in);
116+
const mp_raw_code_t *rc = o->rc;
117+
const mp_bytecode_prelude_t *prelude = &rc->prelude;
118+
switch (attr) {
119+
case MP_QSTR_co_code:
120+
dest[0] = mp_obj_new_bytes(
121+
(void *)prelude->opcodes,
122+
rc->fun_data_len - (prelude->opcodes - (const byte *)rc->fun_data)
123+
);
124+
break;
125+
case MP_QSTR_co_consts:
126+
dest[0] = MP_OBJ_FROM_PTR(code_consts(o->context, rc));
127+
break;
128+
case MP_QSTR_co_filename:
129+
dest[0] = MP_OBJ_NEW_QSTR(MP_CODE_QSTR_MAP(o->context, 0));
130+
break;
131+
case MP_QSTR_co_firstlineno:
132+
dest[0] = MP_OBJ_NEW_SMALL_INT(mp_prof_bytecode_lineno(rc, 0));
133+
break;
134+
case MP_QSTR_co_name:
135+
dest[0] = MP_OBJ_NEW_QSTR(MP_CODE_QSTR_MAP(o->context, prelude->qstr_block_name_idx));
136+
break;
137+
case MP_QSTR_co_names:
138+
dest[0] = MP_OBJ_FROM_PTR(o->dict_locals);
139+
break;
140+
case MP_QSTR_co_lnotab:
141+
if (!o->lnotab) {
142+
o->lnotab = raw_code_lnotab(rc);
143+
}
144+
dest[0] = o->lnotab;
145+
break;
146+
}
147+
}
148+
149+
MP_DEFINE_CONST_OBJ_TYPE(
150+
mp_type_code,
151+
MP_QSTR_code,
152+
MP_TYPE_FLAG_NONE,
153+
print, code_print,
154+
attr, code_attr
155+
);
156+
157+
mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc, bool result_required) {
158+
mp_obj_code_t *o;
159+
if (result_required) {
160+
o = m_new_obj(mp_obj_code_t);
161+
} else {
162+
o = m_new_obj_maybe(mp_obj_code_t);
163+
if (o == NULL) {
164+
return MP_OBJ_NULL;
165+
}
166+
}
167+
o->base.type = &mp_type_code;
168+
o->context = context;
169+
o->rc = rc;
170+
o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly?
171+
o->lnotab = MP_OBJ_NULL;
172+
return MP_OBJ_FROM_PTR(o);
173+
}
174+
175+
#endif

0 commit comments

Comments
 (0)
0