docs(objects): explain wordcode operations and codec boundaries
This commit is contained in:
@@ -1,48 +1,87 @@
|
||||
//! 编译器与 VM 共享的指令协议:编译器构造逻辑指令,编码器产生双字节代码单元,
|
||||
//! EndorCode 构造时解码并验证,eval 消费已解码指令;这里不执行指令或维护运行栈。
|
||||
|
||||
use crate::{CompareOp, VmError};
|
||||
|
||||
/// Endor wordcode, following CPython's opcode/oparg code-unit design.
|
||||
/// Numeric opcode IDs are Endor's own, not a CPython bytecode ABI.
|
||||
/// Endor 自有操作码;每个物理代码单元含 opcode/oparg 两字节。
|
||||
/// 采用类似 CPython 的单元设计,但编号与其字节码 ABI 不兼容。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Opcode {
|
||||
/// 无操作,无操作数。
|
||||
Nop = 0,
|
||||
/// 以操作数索引代码常量表,将借用常量保留为栈根。
|
||||
LoadConst = 1,
|
||||
/// 读取 localsplus 局部槽,操作数为槽索引。
|
||||
LoadFast = 2,
|
||||
/// 弹出栈顶写局部槽,执行器先处理对象赋值协议。
|
||||
StoreFast = 3,
|
||||
/// 清除局部槽并归还其根,未绑定时报错。
|
||||
DeleteFast = 4,
|
||||
/// 以 names 索引查全局空间,缺失再查函数捕获的内置空间。
|
||||
LoadGlobal = 5,
|
||||
/// 以 names 索引将栈顶写入全局字典并弹出。
|
||||
StoreGlobal = 6,
|
||||
/// 以 names 索引删除全局键,缺失时报错。
|
||||
DeleteGlobal = 7,
|
||||
/// 丢弃栈顶并释放对应根。
|
||||
PopTop = 8,
|
||||
/// 复制距栈顶第 argument 项(从 1 起),对象值新增栈根。
|
||||
Copy = 9,
|
||||
/// 交换栈顶与第 argument 项,合法深度至少为 2。
|
||||
Swap = 10,
|
||||
/// 压入调用协议专用 Null 标记,不是语言 Nil。
|
||||
PushNull = 11,
|
||||
/// 消费函数、接收者或 Null 及 argument 个显式参数,调用后留下结果。
|
||||
Call = 12,
|
||||
/// 移出帧结果并清理执行帧,无操作数。
|
||||
ReturnValue = 13,
|
||||
/// 从当前逻辑指令末尾向前跳 argument 个代码单元。
|
||||
JumpForward = 14,
|
||||
/// 从当前逻辑指令末尾向后跳 argument 个代码单元。
|
||||
JumpBackward = 15,
|
||||
/// 弹出条件,假时向前跳 argument 个代码单元。
|
||||
PopJumpIfFalse = 16,
|
||||
/// 弹出条件,真时向前跳 argument 个代码单元。
|
||||
PopJumpIfTrue = 17,
|
||||
/// 把栈顶经真值协议变为布尔值,无操作数。
|
||||
ToBool = 18,
|
||||
/// 对栈顶求真后逻辑取反,无操作数。
|
||||
UnaryNot = 19,
|
||||
/// 比较栈顶两个值;操作数采用 compare/compare_operation 的映射。
|
||||
CompareOp = 20,
|
||||
/// 读取 localsplus 对应 Cell 内值,空 Cell 报未绑定。
|
||||
LoadDeref = 21,
|
||||
/// 把栈顶写入对应 Cell,保持闭包共享槽身份。
|
||||
StoreDeref = 22,
|
||||
/// 清空对应 Cell,后续读取视为未绑定。
|
||||
DeleteDeref = 23,
|
||||
/// 压入对应 Cell 自身句柄,供创建嵌套闭包使用。
|
||||
LoadClosure = 24,
|
||||
/// 以 names 索引绑定栈顶对象属性,以所得值替换 owner 栈项。
|
||||
LoadAttr = 25,
|
||||
/// 以 names 索引把栈中的值写入对象属性。
|
||||
StoreAttr = 26,
|
||||
/// 消费 argument 对键值,构造插入顺序字典。
|
||||
BuildMap = 27,
|
||||
/// 操作数低/高 16 位分别计默认值与捕获槽数量。
|
||||
MakeFunction = 28,
|
||||
/// 消费四个构类输入并压入类型结果,无操作数。
|
||||
BuildClass = 29,
|
||||
/// 观察栈顶等待对象,Pending 保存暂停位置,Ready 替换为结果。
|
||||
Await = 30,
|
||||
/// 操作数指定算术种类,对两个栈值求值并压入结果。
|
||||
BinaryArithmetic = 31,
|
||||
/// 对栈顶数值取负,无操作数。
|
||||
UnaryNegate = 32,
|
||||
/// 编码器专用高位前缀;逻辑指令构造方不得直接提交。
|
||||
ExtendedArg = 255,
|
||||
}
|
||||
|
||||
impl Opcode {
|
||||
/// 物理解码器验证一个操作码字节;未知编号返回 None,不做不安全 enum 转换。
|
||||
fn decode(byte: u8) -> Option<Self> {
|
||||
// 显式白名单保证保留编号不会误解为已实现指令。
|
||||
Some(match byte {
|
||||
0 => Self::Nop,
|
||||
1 => Self::LoadConst,
|
||||
@@ -82,6 +121,7 @@ impl Opcode {
|
||||
})
|
||||
}
|
||||
|
||||
/// 编解码共用的操作数分类;无操作数指令只能携带零且不得有扩展前缀。
|
||||
pub(crate) fn takes_argument(self) -> bool {
|
||||
!matches!(
|
||||
self,
|
||||
@@ -98,25 +138,28 @@ impl Opcode {
|
||||
}
|
||||
}
|
||||
|
||||
/// A decoded logical instruction; EXTENDED_ARG is handled by the codec.
|
||||
/// 编译器构造的逻辑指令;完整操作数为 u32,扩展前缀由编解码器处理。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Instruction {
|
||||
/// 逻辑操作,不应手工指定 ExtendedArg。
|
||||
pub opcode: Opcode,
|
||||
/// 完整操作数;具体含义由 opcode 决定,构造时尚未做代码布局校验。
|
||||
pub argument: u32,
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
/// 编译器/宿主组装指令;这里只赋值,合法索引和栈效果由 EndorCode 校验。
|
||||
pub const fn new(opcode: Opcode, argument: u32) -> Self {
|
||||
Self { opcode, argument }
|
||||
}
|
||||
/// 编译器发出无参数指令的便捷入口,把 argument 明确置零。
|
||||
pub const fn simple(opcode: Opcode) -> Self {
|
||||
Self::new(opcode, 0)
|
||||
}
|
||||
|
||||
/// Build a function from [defaults..., closure cells..., code] on the stack.
|
||||
/// The low 16 operand bits count trailing positional defaults; the high
|
||||
/// 16 bits count cells in the code's free-variable order. No tuples or
|
||||
/// subsequent mutation of the function object are required.
|
||||
/// 编译器为栈布局 [默认值..., 捕获 Cell..., code] 生成函数创建指令。
|
||||
/// 低 16 位计末尾位置参数默认值,高 16 位计按自由变量顺序排列的捕获槽。
|
||||
/// VM 一次构造函数,不需要额外元组或事后修改函数字段。
|
||||
pub const fn make_function(defaults: u16, closure: u16) -> Self {
|
||||
Self::new(
|
||||
Opcode::MakeFunction,
|
||||
@@ -124,6 +167,7 @@ impl Instruction {
|
||||
)
|
||||
}
|
||||
|
||||
/// 编译器将比较运算转成固定操作数,与下方反向解码表配对。
|
||||
pub const fn compare(operation: CompareOp) -> Self {
|
||||
Self::new(
|
||||
Opcode::CompareOp,
|
||||
@@ -138,29 +182,36 @@ impl Instruction {
|
||||
)
|
||||
}
|
||||
|
||||
/// Decode a comparison operand; None denotes an invalid operand.
|
||||
/// VM/校验器解析比较种类;超出六个固定值时返回 None。
|
||||
pub fn compare_operation(argument: u32) -> Option<CompareOp> {
|
||||
compare_operation(argument)
|
||||
}
|
||||
|
||||
/// Decode the default-value and closure-cell counts of MAKE_FUNCTION.
|
||||
/// VM 创建函数及栈校验器拆出默认值数、闭包槽数,不访问运行栈。
|
||||
pub fn function_counts(argument: u32) -> (usize, usize) {
|
||||
function_counts(argument)
|
||||
}
|
||||
|
||||
/// Encodes 8-bit operands directly and prefixes up to three EXTENDED_ARG
|
||||
/// code units for larger operands. Each code unit is [opcode, operand].
|
||||
/// EndorCode::from_instructions 调用:小操作数直接编码,大操作数最多用三个前缀。
|
||||
/// 每单元两字节,前缀携带高位;不在这里重算跳转距离或校验常量/槽索引。
|
||||
pub fn encode_all(instructions: &[Self]) -> Result<Vec<u8>, VmError> {
|
||||
// 逐条追加;报错时局部缓冲丢弃,不向调用者返回部分编码。
|
||||
let mut bytes = Vec::new();
|
||||
// 逻辑列表中每项最终对应一个非前缀单元及零到三个前缀单元。
|
||||
for instruction in instructions {
|
||||
// 前缀由完整 u32 操作数推导,拒绝手工插入导致分组歧义。
|
||||
if instruction.opcode == Opcode::ExtendedArg {
|
||||
return Err(invalid(0, "EXTENDED_ARG is emitted by the encoder"));
|
||||
}
|
||||
// 无参数操作携带非零数据是协议错误,报告当前输出的代码单元位置。
|
||||
if !instruction.opcode.takes_argument() && instruction.argument != 0 {
|
||||
return Err(invalid(bytes.len() / 2, "unexpected instruction operand"));
|
||||
}
|
||||
// 大端拆分方便先发高位前缀,最低字节始终放在最终 opcode 单元。
|
||||
let parts = instruction.argument.to_be_bytes();
|
||||
// 跳过前导零;全零时选最后字节,保证仍发出一个有效单元。
|
||||
let first = parts.iter().position(|part| *part != 0).unwrap_or(3);
|
||||
// 只输出所需高位,最长三项,不把最低字节重复编码。
|
||||
for part in &parts[first..3] {
|
||||
bytes.extend([Opcode::ExtendedArg as u8, *part]);
|
||||
}
|
||||
@@ -172,15 +223,18 @@ impl Instruction {
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct DecodedInstruction {
|
||||
/// Offsets count two-byte code units, including EXTENDED_ARG prefixes.
|
||||
/// 逻辑指令起点(包括首个前缀),单位是双字节代码单元。
|
||||
pub offset: usize,
|
||||
/// 最后一个实际 opcode 单元之后的位置,作为相对跳转的基点。
|
||||
pub next_offset: usize,
|
||||
/// 已合并前缀的完整逻辑指令。
|
||||
pub instruction: Instruction,
|
||||
}
|
||||
|
||||
impl DecodedInstruction {
|
||||
/// Resolve a jump relative to the end of this logical instruction.
|
||||
/// VM/校验器相对逻辑指令末尾解析跳转;此处不检查目标是否为有效指令边界。
|
||||
pub fn jump_target(self) -> Result<usize, VmError> {
|
||||
// 非跳转操作不能借用跳转计算;条件跳转按前向距离解释。
|
||||
if !matches!(
|
||||
self.instruction.opcode,
|
||||
Opcode::JumpForward
|
||||
@@ -194,37 +248,52 @@ impl DecodedInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/// EndorCode::with_layout 调用,将物理单元分组成逻辑指令;后续 verify 检查控制流与栈。
|
||||
pub(crate) fn decode(bytes: &[u8]) -> Result<Vec<DecodedInstruction>, VmError> {
|
||||
// 拒绝空代码与半个单元,保证下方 cursor+1 总能访问操作数字节。
|
||||
if bytes.is_empty() || !bytes.len().is_multiple_of(2) {
|
||||
return Err(invalid(
|
||||
0,
|
||||
"wordcode must contain complete two-byte code units",
|
||||
));
|
||||
}
|
||||
// 输出逻辑指令列表与物理字节游标分开,前缀不会独立进入输出。
|
||||
let mut instructions = Vec::new();
|
||||
// cursor 以字节计,保存到 offset/next_offset 时再除以二。
|
||||
let mut cursor = 0;
|
||||
// 外层每次完整读一条逻辑指令,内层吸收连续高位前缀。
|
||||
while cursor < bytes.len() {
|
||||
// 记住首前缀/实际指令的起点,错误定位与跳转边界均用它。
|
||||
let start = cursor / 2;
|
||||
// 从高到低合并最多四个操作数字节。
|
||||
let mut argument = 0_u32;
|
||||
// 前缀最多三个,第四个将溢出 u32 编码容量。
|
||||
let mut prefixes = 0;
|
||||
// 遇到实际 opcode 才完成本条;流末尾仍只有前缀时报告悬空前缀。
|
||||
loop {
|
||||
// 前缀之后必须还有完整单元,不能将截断流视作正常结束。
|
||||
if cursor >= bytes.len() {
|
||||
return Err(invalid(start, "dangling EXTENDED_ARG"));
|
||||
}
|
||||
// 验证编号后再组合操作数,未知编号的错误位置指向该物理单元。
|
||||
let opcode = Opcode::decode(bytes[cursor])
|
||||
.ok_or_else(|| invalid(cursor / 2, "unknown opcode"))?;
|
||||
argument = (argument << 8) | u32::from(bytes[cursor + 1]);
|
||||
cursor += 2;
|
||||
// 前缀只累积数据,不压入逻辑指令列表。
|
||||
if opcode == Opcode::ExtendedArg {
|
||||
prefixes += 1;
|
||||
// 在读取第五个数据字节之前拒绝,防截断高位。
|
||||
if prefixes > 3 {
|
||||
return Err(invalid(start, "too many EXTENDED_ARG prefixes"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 即使扩展数据全零,无参数指令也不允许前缀。
|
||||
if !opcode.takes_argument() && (argument != 0 || prefixes != 0) {
|
||||
return Err(invalid(start, "unexpected instruction operand"));
|
||||
}
|
||||
// 起点保留首前缀位置,末尾记录已消费单元之后的位置。
|
||||
instructions.push(DecodedInstruction {
|
||||
offset: start,
|
||||
next_offset: cursor / 2,
|
||||
@@ -236,6 +305,7 @@ pub(crate) fn decode(bytes: &[u8]) -> Result<Vec<DecodedInstruction>, VmError> {
|
||||
Ok(instructions)
|
||||
}
|
||||
|
||||
/// Instruction 公共查询和代码校验共用的比较编号反向映射。
|
||||
pub(crate) fn compare_operation(argument: u32) -> Option<CompareOp> {
|
||||
Some(match argument {
|
||||
0 => CompareOp::Lt,
|
||||
@@ -248,10 +318,12 @@ pub(crate) fn compare_operation(argument: u32) -> Option<CompareOp> {
|
||||
})
|
||||
}
|
||||
|
||||
/// 解出 MAKE_FUNCTION 的两个无符号计数;合法槽布局由 EndorCode/执行器另行检查。
|
||||
pub(crate) fn function_counts(argument: u32) -> (usize, usize) {
|
||||
((argument & 0xffff) as usize, (argument >> 16) as usize)
|
||||
}
|
||||
|
||||
/// 编解码与代码校验统一构造带代码单元偏移的错误,不把偏移转成源文件行号。
|
||||
pub(crate) fn invalid(offset: usize, reason: &'static str) -> VmError {
|
||||
VmError::InvalidBytecode { offset, reason }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user