为什么`PyCodeObject`中的`co_code_adaptive`会转换为`_PyCode_CODE`宏中的`uint16_t`指针?

问题描述 投票:0回答:1

我在探索 Python 3.11 源代码时遇到了

_PyCode_CODE
宏。我注意到在这个宏中,
co_code_adaptive
PyCodeObject
成员被转换为
uint16_t*
指针。但是,
co_code_adaptive
被定义为长度为 1 的
char
数组。


/*******************************************/
// code in code.h
/*******************************************/
#define _PyCode_CODE(co) ((const uint16_t *)(co)->co_code_adaptive)

#define _PyCode_DEF(SIZE) {                                                    \
    // ...                                                                     \
    char co_code_adaptive[(SIZE)];                                             \
}
/* Bytecode object */
struct PyCodeObject _PyCode_DEF(1);
/*******************************************/
// code in specialize.c
/*******************************************/
void
_PyCode_Quicken(PyCodeObject *code)
{
    _Py_QuickenedCount++;
    int previous_opcode = -1;
    _Py_CODEUNIT *instructions = _PyCode_CODE(code);
    for (int i = 0; i < Py_SIZE(code); i++) {
        int opcode = _Py_OPCODE(instructions[i]);
// ...
}

这种转换似乎可能会带来内存访问风险,因为 char 和 uint16_t 具有不同的类型大小和对齐要求。为什么Python开发者选择这样实现呢?在这种情况下如何保证内存安全?

具体来说,在

specialize.c
文件中,
_PyCode_Quicken
函数内,该宏用于定义一个名为
uint16_t*
instructions
指针。然后代码使用
[]
运算符访问内存。 Python 如何确保这些内存访问有效?

任何对底层设计决策或解释此方法的文档的见解将不胜感激。

python c python-3.x python-internals memory-safety
1个回答
0
投票

对于 Python,每个位码指令都是一个两字节对象1,带有操作码和相应的参数:

typedef struct _CodeUnit {
    uint8_t opcode;
    uint8_t oparg;
} _CodeUnit;

实际的 PyCodeObject 结构是使用 C 中的“灵活数组成员”习惯用法来分配的,以减少分配(如果没有该习惯用法,我们将需要为 PyCodeObject 结构分配一次,为编译后的位码分配一次)。这种模式在实现中经常使用。

这意味着可以像这样分配一个 PyCodeObject 及其相应的代码:

PyCodeObject *code = malloc(sizeof(PyCodeObject) + 2*instruction_count);

指令数量编码在由

ob_size
给出的
PyVarObject
成员中。这就是
Py_SIZE
在指令循环中检索的内容。

因此,访问通过构造是有效的。

1。 出于本答案的目的,我忽略了用于某些指令的内联缓存。

© www.soinside.com 2019 - 2024. All rights reserved.