diff --git a/crates/objects/src/instruction.rs b/crates/objects/src/instruction.rs index 0d3d17f..217d2ca 100644 --- a/crates/objects/src/instruction.rs +++ b/crates/objects/src/instruction.rs @@ -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 { + // 显式白名单保证保留编号不会误解为已实现指令。 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 { 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, 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 { + // 非跳转操作不能借用跳转计算;条件跳转按前向距离解释。 if !matches!( self.instruction.opcode, Opcode::JumpForward @@ -194,37 +248,52 @@ impl DecodedInstruction { } } +/// EndorCode::with_layout 调用,将物理单元分组成逻辑指令;后续 verify 检查控制流与栈。 pub(crate) fn decode(bytes: &[u8]) -> Result, 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, VmError> { Ok(instructions) } +/// Instruction 公共查询和代码校验共用的比较编号反向映射。 pub(crate) fn compare_operation(argument: u32) -> Option { Some(match argument { 0 => CompareOp::Lt, @@ -248,10 +318,12 @@ pub(crate) fn compare_operation(argument: u32) -> Option { }) } +/// 解出 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 } }