microPython的源码解析之 emitbc.c

本文详细解读MicroPython的emitbc.c源码,探讨其字节码生成器的实现,涉及加载常量、调用函数、异常处理等操作。通过了解这个微型Python解释器的内部逻辑,读者可以更好地理解Python解析器的核心实现。
摘要由CSDN通过智能技术生成

MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.

这段代码是一个C语言的源文件,它实现了一个微型Python解释器的字节码生成器。代码中包含了很多与生成和操作Python字节码相关的函数。

#include <stdbool.h> // 包含布尔类型的定义
#include <stdint.h> // 包含固定宽度整数类型的定义
#include <stdio.h> // 包含标准输入输出库
#include <string.h> // 包含字符串操作函数
#include <unistd.h> // 包含UNIX标准工具库
#include <assert.h> // 包含断言宏

// 包含MicroPython特有的头文件
#include "py/mpstate.h"
#include "py/smallint.h"
#include "py/emit.h"
#include "py/bc0.h"

// 条件编译,只有在启用编译器的情况下才编译以下代码
#if MICROPY_ENABLE_COMPILER

// 定义一个临时数据的大小,用于编码最大的无符号整数
#define DUMMY_DATA_SIZE (MP_ENCODE_UINT_MAX_BYTES)

// 定义字节码发射器的结构体
struct _emit_t {
   
    // 临时数据,用于存储字节码和代码信息
    byte dummy_data[DUMMY_DATA_SIZE];

    // 当前遍历的阶段
    pass_kind_t pass : 8;

    // 如果设置为true,则代码生成器应该抑制发射代码,因为它是死代码
    bool suppress;

    // 栈大小
    int stack_size;

    // 指向公共发射器的指针
    mp_emit_common_t *emit_common;
    // 指向当前作用域的指针
    scope_t *scope;

    // 上一条源代码行的偏移量和行号
    mp_uint_t last_source_line_offset;
    mp_uint_t last_source_line;

    // 标签的最大数量
    size_t max_num_labels;
    // 标签偏移量的数组
    size_t *label_offsets;

    // 代码信息的偏移量和大小
    size_t code_info_offset;
    size_t code_info_size;
    // 字节码的偏移量和大小
    size_t bytecode_offset;
    size_t bytecode_size;
    // 存储字节码和代码信息的指针
    byte *code_base;
    // 标记是否溢出
    bool overflow;

    // 信息的数量和单元格的数量
    size_t n_info;
    size_t n_cell;
};

// 创建一个新的字节码发射器
emit_t *emit_bc_new(mp_emit_common_t *emit_common) {
   
    emit_t *emit = m_new0(emit_t, 1);
    emit->emit_common = emit_common;
    return emit;
}

// 设置发射器的最大标签数量
void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels) {
   
    emit->max_num_labels = max_num_labels;
    emit->label_offsets = m_new(size_t, emit->max_num_labels);
}

// 释放发射器占用的资源
void emit_bc_free(emit_t *emit) {
   
    m_del(size_t, emit->label_offsets, emit->max_num_labels);
    m_del_obj(emit_t, emit);
}

// 通过这个函数来发射代码信息
static uint8_t *emit_get_cur_to_write_code_info(void *emit_in, size_t num_bytes_to_write) {
   
    emit_t *emit = emit_in;
    if (emit->pass < MP_PASS_EMIT) {
   
        // 在非发射阶段,更新偏移量但不写入实际数据
        emit->code_info_offset += num_bytes_to_write;
        return emit->dummy_data;
    } else {
   
        // 在发射阶段,确保写入的字节不会超出代码信息的大小
        assert(emit->code_info_offset + num_bytes_to_write <= emit->code_info_size);
        byte *c = emit->code_base + emit->code_info_offset;
        emit->code_info_offset += num_bytes_to_write;
        return c;
    }
}

// 发射一个字节的代码信息
static void emit_write_code_info_byte(emit_t *emit, byte val) {
   
    *emit_get_cur_to_write_code_info(emit, 1) = val;
}

// 发射一个qstr(字符串)的代码信息
static void emit_write_code_info_qstr(emit_t *emit, qstr qst) {
   
    // 编码并发射qstr
    mp_encode_uint(emit, emit_get_cur_to_write_code_info, mp_emit_common_use_qstr(emit->emit_common, qst));
}

#if MICROPY_ENABLE_SOURCE_LINE
// 发射源代码行号信息
static void emit_write_code_info_bytes_lines(emit_t *emit, mp_uint_t bytes_to_skip, mp_uint_t lines_to_skip) {
   
    assert(bytes_to_skip > 0 || lines_to_skip > 0);
    while (bytes_to_skip > 0 || lines_to_skip > 0) {
   
        mp_uint_t b, l;
        if (lines_to_skip <= 6 || bytes_to_skip > 0xf) {
   
            // 使用0b0LLBBBBB编码
            b = MIN(bytes_to_skip, 0x1f);
            if (b < bytes_to_skip) {
   
                // 无法跳过任何行,直到跳过所有字节
                l = 0;
            } else {
   
                l = MIN(lines_to_skip, 0x3);
            }
            *emit_get_cur_to_write_code_info(emit, 1) = b | (l << 5);
        } else {
   
            // 使用0b1LLLBBBB 0bLLLLLLLL编码(低位在第二字节)
            b = MIN(bytes_to_skip, 0xf);
            l = MIN(lines_to_skip, 0x7ff);
            byte *ci = emit_get_cur_to_write_code_info(emit, 2);
            ci[0] = 0x80 | b | ((l >> 4) & 0x70);
            ci[1] = l;
        }
        bytes_to_skip -= b;
        lines_to_skip -= l;
    }
}
#endif

// 通过这个函数来发射字节码
static uint8_t *emit_get_cur_to_write_bytecode(void *emit_in, size_t num_bytes_to_write) {
   
    emit_t *emit = emit_in;
    if (emit->suppress) {
   
        // 如果抑制发射,则不写入实际数据
        return emit->dummy_data;
    }
    if (emit->pass < MP_PASS_EMIT) {
   
        // 在非发射阶段,更新偏移量但不写入实际数据
        emit->bytecode_offset += num_bytes_to_write;
        return emit->dummy_data;
    } else {
   
        // 在发射阶段,确保写入的字节不会超出字节码的大小
        assert(emit->bytecode_offset + num_bytes_to_write <= emit->bytecode_size);
        byte *c = emit->code_base + emit->code_info_size + emit->bytecode_offset;
        emit->bytecode_offset += num_bytes_to_write;
        return c;
    }
}

// 发射一个字节的原始字节码
static void emit_write_bytecode_raw_byte(emit_t *emit, byte b1) {
   
    byte *c = emit_get_cur_to_write_bytecode(emit, 1);
    c[0] = b1;
}

// 发射一个字节的字节码,并调整栈大小
static void emit_write_bytecode_byte(emit_t *emit, int stack_adj, byte b1) {
   
    mp_emit_bc_adjust_stack_size(emit, stack_adj);
    byte *c = emit_get_cur_to_write_bytecode(emit, 1);
    c[0] = b1;
}

// 发射一个字节的字节码,并编码一个整数
static void emit_write_bytecode_byte_int(emit_t *emit, int stack_adj, byte b1, mp_int_t num) {
   
    emit_write_bytecode_byte(emit, stack_adj, b1);

    // 将每个7位存储在一个单独的字节中,这就是需要的字节数
    byte buf[MP_ENCODE_UINT_MAX_BYTES];
    byte *p = buf + sizeof(buf);
    // 我们以小端序编码,但以大端序存储,以帮助解码
    do {
   
        *--p = num & 0x7f;
        num >>= 7;
    } while (num != 0 && num != -1);
    // 确保我们存储的最高位(掩码0x40)与数字的符号相匹配
    // 如果不是,则存储一个额外的字节只为了编码符号
    if (num == -1 && (*p & 0x40) == 0) {
   
        *--p = 0x7f;
    } else if (num == 0 && (*p & 0x40) != 0) {
   
        *--p = 0;
    }

    byte *c = emit_get_cur_to_write_bytecode(emit, buf + sizeof(buf) - p);
    while (p != buf + sizeof(buf) - 1) {
   
        *c++ = *p++ | 0x80;
    }
    *c = *p;
}

// 发射一个字节的字节码,并编码一个无符号整数
static void emit_write_bytecode_byte_uint(emit_t *emit, int stack_adj, byte b, mp_uint_tval) {
   
    emit_write_bytecode_byte(emit, stack_adj, b);
    mp_encode_uint(emit, emit_get_cur_to_write_bytecode, val);
}

// 发射一个字节的字节码,并编码一个常量
static void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n) {
   
    emit_write_bytecode_byte_uint(emit, stack_adj, b, n);
}

// 发射一个字节的字节码,并编码一个字符串
static void emit_write_bytecode_byte_qstr(emit_t *emit, int stack_adj, byte b, qstr qst) {
   
    emit_write_bytecode_byte_uint(emit, stack_adj, b, mp_emit_common_use_qstr(emit->emit_common, qst));
}

// 发射一个字节的字节码,并编码一个对象
static void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) {
   
    emit_write_bytecode_byte_const(emit, stack_adj, b, mp_emit_common_use_const_obj(emit->emit_common, obj));
}

// 发射一个字节的字节码,并编码一个子代码对象
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
    // 设置子代码对象的行号定义
    rc->line_of_definition = emit->last_source_line;
  • 26
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值