MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计.
#include <stdbool.h>
#include <string.h>
#include <assert.h>
#include "py/bc0.h"
#include "py/bc.h"
#include "py/objfun.h"
#if MICROPY_DEBUG_VERBOSE // 打印调试信息
#define DEBUG_PRINT (1)
#else // 不打印调试信息
#define DEBUG_PRINT (0)
#define DEBUG_printf(...) (void)0
#endif
void mp_encode_uint(void *env, mp_encode_uint_allocator_t allocator, mp_uint_t val) {
// 我们将每个7位存储在一个单独的字节中,这就是需要的字节数
byte buf[MP_ENCODE_UINT_MAX_BYTES];
byte *p = buf + sizeof(buf);
// 我们以小端序编码,但以大端序存储,以帮助解码
do {
*--p = val & 0x7f;
val >>= 7;
} while (val != 0);
byte *c = allocator(env, buf + sizeof(buf) - p);
if (c != NULL) {
while (p != buf + sizeof(buf) - 1) {
*c++ = *p++ | 0x80;
}
*c = *p;
}
}
mp_uint_t mp_decode_uint(const byte **ptr) {
mp_uint_t unum = 0;
byte val;
const byte *p = *ptr;
do {
val = *p++;
unum = (unum << 7) | (val & 0x7f);
} while ((val & 0x80) != 0);
*ptr = p;
return unum;
}
// 这个函数用于帮助减少调用者栈的使用,对于调用者不需要增加ptr参数的情况。如果ptr是一个局部变量
// 并且调用者使用mp_decode_uint(&ptr)而不是这个函数,那么编译器必须为ptr分配一个栈槽,
// 并且因为这个指针可能存储在全局并在函数中稍后重用,所以这个槽不能被其他东西重用。
mp_uint_t mp_decode_uint_value(const byte *ptr) {
return mp_decode_uint(&ptr);
}
// 这个函数用于帮助减少调用者栈的使用,对于调用者不需要实际值,只想跳过它的情况。
const byte *mp_decode_uint_skip(const byte *ptr) {
while ((*ptr++) & 0x80) {
}
return ptr;
}
static NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) {
#if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE
// 通用消息,也用于其他参数问题
(void)f;
(void)expected;
(void)given;
mp_arg_error_terse_mismatch();
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
(void)f;
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("函数需要 %d 个位置参数,但给出了 %d 个"), expected, given);
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
mp_raise_msg_varg(&mp_type_TypeError,
MP_ERROR_TEXT("%q() 需要 %d 个位置参数,但给出了 %d 个"),
mp_obj_fun_get_name(MP_OBJ_FROM_PTR(f)), expected, given);
#endif
}
#if DEBUG_PRINT
static void dump_args(const mp_obj_t *a, size_t sz) {
DEBUG_printf("%p: ", a);
for (size_t i = 0; i < sz; i++) {
DEBUG_printf("%p ", a[i]);
}
DEBUG_printf("\n");
}
#else
#define dump_args(...) (void)0
#endif
// 进入时,code_state 应该在某处分配(栈/堆),并且包含以下有效条目:
// - code_state->fun_bc 应包含函数对象的指针
// - code_state->ip 应包含指向序言开头的指针
// - code_state->sp 应该是:&code_state->state[0] - 1
// - code_state->n_state 应该是本地状态中对象的数量
static void mp_setup_code_state_helper(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// 这个函数相当复杂。它的主要目的是在速度和RAM使用方面高效地处理常见情况,即只有位置参数。
// 获取我们想要设置的函数对象(可以是字节码或本地代码)
mp_obj_fun_bc_t *self = code_state->fun_bc;
// 获取缓存的 n_state(而不是再次解码它)
size_t n_state = code_state->n_state;
// 解码序言
size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args;
MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args