MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.
microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
python编程示例系列 python编程示例系列二
python的Web神器Streamlit
这段代码定义了一个函数mp_prof_print_instr
,它用于打印微Python字节码指令的详细信息。函数首先通过mp_prof_opcode_decode
解码当前指令,然后根据解码结果和前导信息(包括源文件名、代码块名称等)打印出详细的指令信息。这些信息包括指令的地址、操作码、操作数、参数对象等。通过这些信息,开发者可以更好地理解和调试微Python代码的执行过程。
#include "py/profile.h" // 包含 profiling 相关的头文件
#include "py/bc0.h" // 包含字节码相关的头文件
#include "py/gc.h" // 包含垃圾回收机制的头文件
#include "py/objfun.h" // 包含对象和函数相关的头文件
#if MICROPY_PY_SYS_SETTRACE // 如果启用了系统跟踪功能
#if !MICROPY_PERSISTENT_CODE_SAVE // 检查是否启用了代码持久化保存功能
// settrace 功能要求我们维护额外的元数据在原始的代码对象上,这通常只在写入 .mpy 文件时进行。
#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_PERSISTENT_CODE_SAVE to be enabled" // 如果没有启用,将报错提示需要启用该功能
#endif
#define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) // 定义宏,用于获取当前线程的 profiling 回调函数
#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) {
const mp_bytecode_prelude_t *prelude = &rc->prelude; // 获取代码的前奏部分
return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc); // 根据行信息和行信息顶部,获取字节码对应的行号
}
// 从字节码中提取前奏部分
void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude) {
const byte *ip = bytecode; // 指向当前字节码的指针
// 解析前奏部分的签名
MP_BC_PRELUDE_SIG_DECODE(ip);
prelude->n_state = n_state; // 设置状态数量
prelude->n_exc_stack = n_exc_stack; // 设置异常栈数量
prelude->scope_flags = scope_flags; // 设置作用域标志
prelude->n_pos_args = n_pos_args; // 设置位置参数数量
prelude->n_kwonly_args = n_kwonly_args; // 设置关键字参数数量
prelude->n_def_pos_args = n_def_pos_args; // 设置默认位置参数数量
// 解析前奏部分的大小
MP_BC_PRELUDE_SIZE_DECODE(ip);
prelude->line_info_top = ip + n_info; // 设置行信息顶部的位置
prelude->opcodes = ip + n_info + n_cell; // 设置操作码的位置
// 解析并设置块名称索引
prelude->qstr_block_name_idx = mp_decode_uint_value(ip);
for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) {
ip = mp_decode_uint_skip(ip); // 跳过参数的解码
}
prelude->line_info = ip; // 设置行信息的开始位置
}
// 打印 code 对象
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); // 将输入的对象转换为 code 对象
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 // 打印定义行号
);
}
// 获取 code 对象的常量
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]); // 创建一个新的 code 对象
if (code == MP_OBJ_NULL) {
m_malloc_fail(sizeof(mp_obj_code_t)); // 如果创建失败,报告内存分配失败
}
consts->items[const_no++] = code; // 将新创建的 code 对象添加到元组中
}
consts->items[const_no++] = mp_const_none; // 添加一个 None 对象作为占位符
return consts; // 返回创建的元组对象
}
// 获取原始代码的 lnotab 信息
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; // 经验值
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; // 指令差异
// 确保差异值不会超过 255
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; // 写入行号差异
}
}
// 创建一个新的字节对象,表示 lnotab 信息
mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index