//! 运算符域:二元/一元/条件/逻辑/更新/`in` 表达式 emit 与分发。 //! 逻辑与 `??` 用短路跳转,复合求值利用 `is_side_effect_free` 优化。 //! 函数:`emit_operator` 及各类 `emit_*_expression`。 use crate::{is_side_effect_free, BinaryOperator, CompileCtx, Emitter}; use oxide_bytecode::module::Constant; use oxide_bytecode::opcode::OpCode; use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; use oxide_parser::{ChainElement, Expression, LogicalOperator, SimpleAssignmentTarget, UnaryOperator, UpdateOperator}; impl Emitter { fn emit_private_in_expression( &self, pin: &oxide_parser::PrivateInExpression, ctx: &mut CompileCtx, ) -> Result { let obj_reg = self.emit_expression(&pin.right, ctx)?; let key_reg = self.emit_private_id_reg(pin.left.name.as_str(), ctx)?; let (brand_reg, brand_id) = self.private_access_brand(obj_reg, pin.left.name.as_str(), ctx)?; let result_reg = ctx.alloc_reg(); ctx.inst(Inst::private_brand_in( Operand::Reg(result_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg), brand_reg, brand_id, )); Ok(result_reg) } fn emit_binary_expression( &self, bin: &oxide_parser::BinaryExpression, ctx: &mut CompileCtx, ) -> Result { // Addition 左结合链摊平:≥3 操作数合并为单条 CONCAT_N(单趟预分配拼接, // 消除 N-2 次中间串分配)。只下钻 left 链、right 括号不拆(a+(b+c) 退化 ADD), // 保 f64 结合性与源码求值序。 if bin.operator == BinaryOperator::Addition { let mut operands = Vec::new(); collect_add_operands(&bin.left, &mut operands); operands.push(&bin.right); // 上限防寄存器压力:CONCAT_N 单点读全部操作数(须同时存活),超长链回退 // 左结合 ADD(寄存器随链复用,纯表达式寄存器占用有界)。 if operands.len() >= 3 && operands.len() <= MAX_CONCAT_N_OPERANDS { let mut regs = Vec::with_capacity(operands.len()); for op in &operands { regs.push(self.emit_expression(op, ctx)?); } let result_reg = ctx.alloc_reg(); ctx.inst(Inst::concat_n(Operand::Reg(result_reg), ®s)); return Ok(result_reg); } } let left = self.emit_expression(&bin.left, ctx)?; let right = self.emit_expression(&bin.right, ctx)?; let op = match bin.operator { BinaryOperator::Addition => OpCode::ADD, BinaryOperator::Subtraction => OpCode::SUB, BinaryOperator::Multiplication => OpCode::MUL, BinaryOperator::Division => OpCode::DIV, BinaryOperator::Remainder => OpCode::MOD, BinaryOperator::Exponential => OpCode::EXP, BinaryOperator::BitwiseAnd => OpCode::BIT_AND, BinaryOperator::BitwiseOR => OpCode::BIT_OR, BinaryOperator::BitwiseXOR => OpCode::BIT_XOR, BinaryOperator::ShiftLeft => OpCode::SHL, BinaryOperator::ShiftRight => OpCode::SHR, BinaryOperator::ShiftRightZeroFill => OpCode::USHR, BinaryOperator::Equality => OpCode::EQ, BinaryOperator::Inequality => OpCode::NEQ, BinaryOperator::LessThan => OpCode::LT, BinaryOperator::GreaterThan => OpCode::GT, BinaryOperator::LessEqualThan => OpCode::LTE, BinaryOperator::GreaterEqualThan => OpCode::GTE, BinaryOperator::In => OpCode::IN, BinaryOperator::Instanceof => OpCode::INSTANCEOF, BinaryOperator::StrictEquality => OpCode::STRICT_EQ, BinaryOperator::StrictInequality => OpCode::STRICT_NEQ, }; ctx.inst(Inst::new(op, Operand::Reg(left), Operand::Reg(left), Operand::Reg(right))); Ok(left) } fn emit_unary_expression(&self, un: &oxide_parser::UnaryExpression, ctx: &mut CompileCtx) -> Result { if matches!(un.operator, UnaryOperator::Delete) { // 括号的引用性透传内层操作数:先逐层剥括号,再按内层形态分派。 let mut operand = &un.argument; while let Expression::ParenthesizedExpression(paren) = operand { operand = &paren.expression; } return self.emit_delete_operand(operand, ctx); } let arg = if matches!(un.operator, UnaryOperator::Typeof) { self.emit_typeof_operand(&un.argument, ctx)? } else { self.emit_expression(&un.argument, ctx)? }; match un.operator { UnaryOperator::UnaryNegation => { ctx.inst(Inst::new(OpCode::NEG, Operand::Reg(arg), Operand::Reg(arg), Operand::None)); Ok(arg) } UnaryOperator::Typeof => { ctx.inst(Inst::new(OpCode::TYPEOF, Operand::Reg(arg), Operand::Reg(arg), Operand::None)); Ok(arg) } UnaryOperator::Void => { ctx.inst(Inst::new(OpCode::VOID, Operand::Reg(arg), Operand::Reg(arg), Operand::None)); Ok(arg) } UnaryOperator::LogicalNot => { ctx.inst(Inst::new(OpCode::NOT, Operand::Reg(arg), Operand::Reg(arg), Operand::None)); Ok(arg) } UnaryOperator::BitwiseNot => { ctx.inst(Inst::new(OpCode::BIT_NOT, Operand::Reg(arg), Operand::Reg(arg), Operand::None)); Ok(arg) } UnaryOperator::UnaryPlus => { ctx.inst(Inst::new(OpCode::UNARY_PLUS, Operand::Reg(arg), Operand::Reg(arg), Operand::None)); Ok(arg) } UnaryOperator::Delete => Err("invalid delete target".into()), } } /// delete 操作数求值:引用形态(标识符/静态成员/计算成员/链式)按引用语义 /// 删除并返回删除结果;非引用操作数返回 true。 /// /// # 步骤 /// 1. 标识符:with 体动态解析、可删全局内置镜像槽先行;其余按 DeleteBinding /// 三分类(见标识符臂注释)——局部绑定与脚本自身顶层已声明名发 false /// 常数,未声明名/隐式全局槽/eval 程序自身顶层 var/函数名发全局对象运行期 /// 探针。严格模式的 delete 标识符是早期错误,由语义分析阶段拦截,不在此出现。 /// 2. 静态/计算成员与链式成员:从对象真删属性,结果为删除返回值。 /// 3. 其余形态(字面量/调用/new/this/一元形/序列/嵌套 delete 等):先求值 /// 操作数(副作用与操作数求值期错误不被吞),再发常量 true。 fn emit_delete_operand(&self, operand: &Expression, ctx: &mut CompileCtx) -> Result { match operand { Expression::Identifier(ident) => { let name = ident.name.as_str(); // with 体内自由标识符:对象有该属性则删除对象属性(返回删除结果), // 否则非严格语义返回 true。 if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) { let obj_reg = ctx.innermost_with_obj().expect("with stack non-empty"); let key_idx = ctx.add_constant(Constant::String(name.to_string())); let key_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx)); let has_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::IN, Operand::Reg(has_reg), Operand::Reg(key_reg), Operand::Reg(obj_reg), )); let fallback_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); ctx.inst(Inst::jmp_if_false(has_reg, fallback_label)); // 复制 obj 到临时寄存器执行删除:DELETE_PROP_DYNAMIC 把结果写回 // rd 槽,直接用它会把 with 对象寄存器覆盖为布尔值。 let tmp_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(tmp_reg), Operand::Reg(obj_reg), Operand::None)); ctx.inst(Inst::new( OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(tmp_reg), Operand::Reg(tmp_reg), Operand::Reg(key_reg), )); let result_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(tmp_reg), Operand::None, )); ctx.inst(Inst::jmp(end_label)); ctx.labels.set_label_pos(fallback_label, ctx.insts.len()); let true_idx = ctx.add_constant(Constant::Boolean(true)); ctx.inst(Inst::load_const(Operand::Reg(result_reg), true_idx)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); return Ok(result_reg); } // 可删全局内置(可写全局名除宿主名 $262):运行期真删 // 全局对象属性(c:true 数据描述符)并返 true;删除成功时清镜像 // 槽,裸读与 globalThis 反射不失步。 if let Some(slot_reg) = ctx.global_builtin_delete_slot(name) { let key_idx = ctx.add_constant(Constant::String(name.to_string())); let reg = ctx.alloc_reg(); ctx.inst(Inst::delete_global_prop_c(Operand::Reg(reg), Operand::Reg(slot_reg), key_idx)); return Ok(reg); } // DeleteBinding 三分类(13.5.1.2 步5 绑定引用走 base.DeleteBinding): // - 局部绑定(捕获 cell/upvalue/lookup 命中函数或块作用域槽)、 // 非 eval 脚本自身顶层已声明名(脚本 var/函数名 c:false)、eval // 程序自身顶层 let/const(落 eval 自身 lexical 环境,declarative // 环境记录 DeleteBinding 恒 false 且不物化全局属性)→ false 常数; // - 其余——未声明名(引用不可解析)、隐式全局槽、eval 程序自身顶层 // var/函数名(物化 c:true 全局属性)——发全局对象运行期探针(与 // Reflect.deleteProperty 同源 DeleteBinding 语义:缺失 → true; // 不可配置 → false 且保留;可配置 → 真删且 true)。三类全局属性的 // c 位已由各自写点物化,探针取值即规范值;独立编译的 eval 程序见 // 不到调用方域变量,静态"当前程序内是否声明"粗于规范动态判定,故 // 不可解析面一律运行期定值。严格模式的 delete 标识符由语义分析 // 提前拦截为早期错误,本臂在 strict 代码不可达。 let local = ctx.captured_bindings.contains_key(name) || ctx.current_upvalue_captures.iter().any(|u| u.name == name); let probe = if local { false } else { match ctx.scopes.symbols.lookup_any_binding(name) { // 未声明名(引用不可解析):运行期按全局属性定值。 None => true, Some((binding, scope_idx)) => { if scope_idx == 0 { // 全局作用域:隐式全局槽(未声明名读写登记,属性 // c:true)与 eval 程序自身顶层 var/函数名(顶层 // var 名集 ∪ 顶层函数声明名,物化 c:true 全局属性) // 可删;eval 顶层 let/const 不在名集内(自身 lexical // 环境不可删),与非 eval 脚本自身顶层 var/函数名 // (c:false)同保留 false。eval 起源随嵌套函数继承, // 子 ctx 内 delete 同一名同样判可删。 ctx.is_implicit_global_reg(binding.reg) || (ctx.is_eval_origin && ctx.global_tier_names.contains(name)) } else { // 函数/块作用域局部绑定:非属性引用,恒 false。 false } } } }; if probe { let key_idx = ctx.add_constant(Constant::String(name.to_string())); let reg = ctx.alloc_reg(); ctx.inst(Inst::delete_global_prop_c(Operand::Reg(reg), Operand::None, key_idx)); // 隐式全局槽被真删后,同程序后续裸读该名须走全局对象属性(顶层 var // 的唯一存储,引擎侧不保留镜像副本):缺失属性读抛 ReferenceError, // delete 的真删效应在读侧可见。 if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) { if ctx.is_implicit_global_reg(binding.reg) { ctx.implicit_global_reads.insert(binding.reg); } } return Ok(reg); } let idx = ctx.add_constant(Constant::Boolean(false)); let reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(reg), idx)); Ok(reg) } Expression::StaticMemberExpression(member) if matches!(&member.object, Expression::Super(_)) => { // super 属性引用不可删除:在求值引用之前直接抛 ReferenceError。 self.emit_throw_error("ReferenceError", "Cannot delete a super reference", ctx) } Expression::ComputedMemberExpression(member) if matches!(&member.object, Expression::Super(_)) => { // super 属性引用不可删除:立即抛 ReferenceError,不执行 ToPropertyKey, // 也不求值基与 this。代价:this 已初始化时键表达式副作用被跳过—— // 未初始化须先抛与副作用须先执行在缺少 GetThisBinding 运行期检查前 // 无法兼得。 self.emit_throw_error("ReferenceError", "Cannot delete a super reference", ctx) } Expression::StaticMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; let prop_name = member.property.name.as_str(); let const_idx = ctx.add_constant(Constant::String(prop_name.to_string())); ctx.inst(Inst::delete_prop_static(Operand::Reg(obj_reg), const_idx as u32)); Ok(obj_reg) } Expression::ComputedMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; let key_reg = self.emit_expression(&member.expression, ctx)?; ctx.inst(Inst::new( OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg), )); Ok(obj_reg) } Expression::ChainExpression(chain) => { let short_label = ctx.next_label_id(); let result_reg = match &chain.expression { ChainElement::StaticMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; if member.optional { let dup_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(obj_reg), Operand::None, )); ctx.inst(Inst::jmp_if_nullish(dup_reg, short_label)); } let prop_name = member.property.name.as_str(); let const_idx = ctx.add_constant(Constant::String(prop_name.to_string())); ctx.inst(Inst::delete_prop_static(Operand::Reg(obj_reg), const_idx as u32)); obj_reg } ChainElement::ComputedMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; if member.optional { let dup_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(obj_reg), Operand::None, )); ctx.inst(Inst::jmp_if_nullish(dup_reg, short_label)); } let key_reg = self.emit_expression(&member.expression, ctx)?; ctx.inst(Inst::new( OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(obj_reg), Operand::Reg(key_reg), )); obj_reg } _ => return Err("invalid delete target".into()), }; let end_label = ctx.next_label_id(); ctx.inst(Inst::jmp(end_label)); ctx.labels.set_label_pos(short_label, ctx.insts.len()); let true_idx = ctx.add_constant(Constant::Boolean(true)); ctx.inst(Inst::load_const(Operand::Reg(result_reg), true_idx)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); Ok(result_reg) } _ => { // 非引用操作数恒 true,但操作数本身须先求值:副作用与操作数 // 求值期错误(如未声明基引用)不被吞。 let _ = self.emit_expression(operand, ctx)?; let idx = ctx.add_constant(Constant::Boolean(true)); let reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(reg), idx)); Ok(reg) } } } /// typeof 操作数求值:未声明标识符特判为 "undefined"。 /// /// # 步骤 /// 1. 剥括号后若为标识符且不在任何作用域/闭包捕获中、非 with 动态解析, /// 发射 LOAD_GLOBAL_TYPEOF:运行期查 global object 属性,命中取真实值 /// (含不在 BUILTIN_GLOBALS 名单的真实全局,如 Temporal),缺失求值 /// undefined(IsUnresolvableReference 语义)。 /// 2. 其余(TDZ 绑定、已声明、with 内)走正常求值:TDZ 抛 ReferenceError、 /// with 动态回退未定义、已声明读真实值。 /// /// # 边界与前提 /// - 未声明标识符不能走 LOAD_GLOBAL(读未声明抛 ReferenceError),故必须在此特判; /// - `typeof x; var x;` / `typeof x; let x;` 分别由 var 预声明(已初始化→undefined) /// 与 TDZ 占位(未初始化→抛)覆盖,不落本分支。 fn emit_typeof_operand(&self, argument: &Expression, ctx: &mut CompileCtx) -> Result { let mut arg_expr = argument; while let Expression::ParenthesizedExpression(p) = arg_expr { arg_expr = &p.expression; } if let Expression::Identifier(ident) = arg_expr { let name = ident.name.as_str(); let in_with_dynamic = !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name); let binding = ctx.scopes.symbols.lookup_any_binding(name); // 捕获集按名保留块级绑定,但块退出后名字已不可解析:此时不再视为 // captured(typeof 对未解析引用求值 "undefined",不得 CELL_GET 误读 // 已失效 cell)。隐式全局登记同样不算真实绑定。 let captured = ctx.current_upvalue_captures.iter().any(|u| u.name == name) || (ctx.captured_bindings.contains_key(name) && ctx.visible_binding_reg(name).is_some()); // 顶层已声明 var:typeof 读全局对象属性(顶层 var 的唯一存储,引擎侧不保留 // 镜像副本),缺失 → "undefined"(非抛,IsUnresolvableReference 语义)。未声明名 // 同走此路(lookup 未命中)。隐式全局槽(未声明名读写登记)同走属性路: // delete 真删后缺失 → "undefined" 而非经引擎侧镜像副本读旧值或抛 // ReferenceError(typeof 对 unresolvable 引用不抛)。 let implicit_global = binding.is_some_and(|(b, _)| ctx.is_implicit_global_reg(b.reg)); let is_tier = self.is_global_tier_name(ctx, name); if !in_with_dynamic && !captured && (is_tier || binding.is_none() || implicit_global) { let key_idx = ctx.add_constant(Constant::String(name.to_string())); let r = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::LOAD_GLOBAL_TYPEOF, Operand::Reg(r), Operand::Const(key_idx), Operand::None, )); return Ok(r); } } self.emit_expression(argument, ctx) } fn emit_conditional_expression( &self, cond: &oxide_parser::ConditionalExpression, ctx: &mut CompileCtx, ) -> Result { let else_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); let test_reg = self.emit_expression(&cond.test, ctx)?; ctx.inst(Inst::jmp_if_false(test_reg, else_label)); let cons_reg = self.emit_expression(&cond.consequent, ctx)?; let result_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(cons_reg), Operand::None, )); ctx.inst(Inst::jmp(end_label)); ctx.labels.set_label_pos(else_label, ctx.insts.len()); let alt_reg = self.emit_expression(&cond.alternate, ctx)?; ctx.inst(Inst::new( OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(alt_reg), Operand::None, )); ctx.labels.set_label_pos(end_label, ctx.insts.len()); Ok(result_reg) } fn emit_logical_expression( &self, log: &oxide_parser::LogicalExpression, ctx: &mut CompileCtx, ) -> Result { let left_reg = self.emit_expression(&log.left, ctx)?; if is_side_effect_free(&log.left) && is_side_effect_free(&log.right) { let right_reg = self.emit_expression(&log.right, ctx)?; let r = ctx.alloc_reg(); let op = match log.operator { LogicalOperator::And => OpCode::AND, LogicalOperator::Or => OpCode::OR, LogicalOperator::Coalesce => OpCode::NULLISH, }; ctx.inst(Inst::new(op, Operand::Reg(r), Operand::Reg(left_reg), Operand::Reg(right_reg))); return Ok(r); } if matches!(log.operator, LogicalOperator::Coalesce) { let rhs_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); let dup_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(left_reg), Operand::None)); ctx.inst(Inst::jmp_if_nullish(dup_reg, rhs_label)); ctx.inst(Inst::jmp(end_label)); ctx.labels.set_label_pos(rhs_label, ctx.insts.len()); let right_reg = self.emit_expression(&log.right, ctx)?; ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(right_reg), Operand::None)); ctx.labels.set_label_pos(end_label, ctx.insts.len()); return Ok(dup_reg); } let skip_label = match log.operator { LogicalOperator::And => ctx.next_label_id(), LogicalOperator::Or => ctx.next_label_id(), LogicalOperator::Coalesce => return Err("invalid logical operator dispatch".into()), }; let dup_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(left_reg), Operand::None)); match log.operator { LogicalOperator::And => ctx.inst(Inst::jmp_if_false(dup_reg, skip_label)), LogicalOperator::Or => ctx.inst(Inst::jmp_if_true(dup_reg, skip_label)), LogicalOperator::Coalesce => return Err("invalid logical operator dispatch".into()), } let right_reg = self.emit_expression(&log.right, ctx)?; ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(dup_reg), Operand::Reg(right_reg), Operand::None)); ctx.labels.set_label_pos(skip_label, ctx.insts.len()); Ok(dup_reg) } fn emit_update_expression( &self, update: &oxide_parser::UpdateExpression, ctx: &mut CompileCtx, ) -> Result { match &update.argument { SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => { let name = id.name.as_str(); // with 体内自由标识符的自增/自减:对象有属性则读写对象,否则回退外层。 if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) { let obj_reg = ctx.innermost_with_obj().expect("with stack non-empty"); let key_idx = ctx.add_constant(Constant::String(name.to_string())); let key_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx)); let has_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::IN, Operand::Reg(has_reg), Operand::Reg(key_reg), Operand::Reg(obj_reg), )); let fallback_label = ctx.next_label_id(); let end_label = ctx.next_label_id(); ctx.inst(Inst::jmp_if_false(has_reg, fallback_label)); // 对象有该属性:读属性 → 增减 → 写回对象。 let old_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::GET_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(old_reg), )); let new_reg = ctx.alloc_reg(); let one_idx = ctx.add_constant(Constant::Int(1)); let one_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); let op = if update.operator == UpdateOperator::Increment { OpCode::ADD } else { OpCode::SUB }; ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg))); ctx.inst(Inst::new( OpCode::SET_PROP_DYNAMIC, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(new_reg), )); let result_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(if update.prefix { new_reg } else { old_reg }), Operand::None, )); ctx.inst(Inst::jmp(end_label)); // 对象无该属性:回退静态自增/自减,结果写入同一 result_reg。 ctx.labels.set_label_pos(fallback_label, ctx.insts.len()); let static_result = self.emit_identifier_update_static(name, update, ctx)?; ctx.inst(Inst::new( OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(static_result), Operand::None, )); ctx.labels.set_label_pos(end_label, ctx.insts.len()); return Ok(result_reg); } // 判断目标是 upvalue 还是被捕获 cell self.emit_identifier_update_static(name, update, ctx) } SimpleAssignmentTarget::StaticMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; let prop_name = member.property.name.as_str(); let key_idx = ctx.add_constant(Constant::String(prop_name.to_string())); let key_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(key_reg), key_idx)); let val_reg = ctx.alloc_reg(); let op = match update.operator { UpdateOperator::Increment => OpCode::MEMBER_INC, UpdateOperator::Decrement => OpCode::MEMBER_DEC, }; match op { OpCode::MEMBER_INC => { ctx.inst(Inst::member_inc(Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg))); } OpCode::MEMBER_DEC => { ctx.inst(Inst::member_dec(Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg))); } _ => unreachable!(), } Ok(val_reg) } SimpleAssignmentTarget::ComputedMemberExpression(member) => { let obj_reg = self.emit_expression(&member.object, ctx)?; // 常量字符串键折叠为 IC 静态路径:MEMBER_INC/DEC 携带 IC 扩展字。 if let Some(key) = crate::expr::member::computed_const_key(&member.expression) { let idx = ctx.add_constant(Constant::String(key)); let key_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(key_reg), idx)); let val_reg = ctx.alloc_reg(); let op = match update.operator { UpdateOperator::Increment => OpCode::MEMBER_INC, UpdateOperator::Decrement => OpCode::MEMBER_DEC, }; match op { OpCode::MEMBER_INC => { ctx.inst(Inst::member_inc( Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg), )); } OpCode::MEMBER_DEC => { ctx.inst(Inst::member_dec( Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg), )); } _ => unreachable!(), } return Ok(val_reg); } let key_reg = self.emit_expression(&member.expression, ctx)?; let val_reg = ctx.alloc_reg(); let op = match update.operator { UpdateOperator::Increment => OpCode::DYN_MEMBER_INC, UpdateOperator::Decrement => OpCode::DYN_MEMBER_DEC, }; ctx.inst(Inst::new(op, Operand::Reg(obj_reg), Operand::Reg(key_reg), Operand::Reg(val_reg))); Ok(val_reg) } _ => Err("member update not yet supported".into()), } } /// 标识符自增/自减的静态路径:upvalue / 被捕获 cell 走显式读-增减-写回, /// 普通槽用 INC_PRE/POST 或 DEC_PRE/POST。返回结果寄存器。 /// /// # 边界与前提 /// - 不可写全局内置槽:值照算(前缀返回新值、后缀返回旧值)但跳过槽写; /// strict 抛 TypeError。局部遮蔽绑定不受影响。 fn emit_identifier_update_static( &self, name: &str, update: &oxide_parser::UpdateExpression, ctx: &mut CompileCtx, ) -> Result { // 自增/自减先解析赋值引用:TDZ 绑定抛 ReferenceError;const 抛 TypeError(编译期拦截)。 // 循环 update 段的 let/const 循环变量是 per-iteration 可变绑定(CreateMutableBinding), // 规范允许写,故豁免 const 检查(register_update_names 覆盖全部循环头声明名)。 self.emit_identifier_tdz_guard(name, ctx)?; if !ctx.register_update_names.iter().any(|n| n == name) { self.emit_const_write_guard(name, ctx)?; } let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name); let captured_cell = ctx.captured_bindings.get(name).copied(); // 循环 update 段:被捕获绑定走寄存器 INC/DEC——C 风格 for 的 let/const // 循环变量每迭代新分配一个 cell,update 写寄存器供其拷入,不污染本迭代 // 闭包捕获的 cell(机制见 `compile_ctx.rs` 的 `register_update_names` 字段文档)。 if ctx.register_update_names.iter().any(|n| n == name) { if let Some(reg) = ctx.scopes.symbols.lookup_any(name) { let op = match (update.operator, update.prefix) { (UpdateOperator::Increment, true) => OpCode::INC_PRE, (UpdateOperator::Increment, false) => OpCode::INC_POST, (UpdateOperator::Decrement, true) => OpCode::DEC_PRE, (UpdateOperator::Decrement, false) => OpCode::DEC_POST, }; if ctx.targets_readonly_builtin(name, reg) { if ctx.is_strict { return self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx); } // 全局不可写内置:值照算(前缀返回新值、后缀返回旧值),跳过槽写。 let tmp_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(tmp_reg), Operand::Reg(reg), Operand::None)); let result_reg = ctx.alloc_reg(); ctx.inst(Inst::new(op, Operand::Reg(tmp_reg), Operand::Reg(result_reg), Operand::Reg(result_reg))); return Ok(result_reg); } let result_reg = ctx.alloc_reg(); ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(result_reg), Operand::Reg(result_reg))); // 可写内置名:RMW 新值同步落全局对象属性。 if ctx.targets_writable_builtin(name, reg) { self.emit_global_put_write(name, reg, ctx); } return Ok(result_reg); } } if let Some(uv) = uv_idx { // upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE; // 后缀形式返回旧值(前缀返回新值)。 let old_reg = ctx.alloc_reg(); ctx.inst(Inst::new( OpCode::LOAD_UPVALUE, Operand::Reg(old_reg), Operand::Imm(uv as u16), Operand::None, )); let one_idx = ctx.add_constant(Constant::Int(1)); let one_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); let op = if update.operator == UpdateOperator::Increment { OpCode::ADD } else { OpCode::SUB }; let new_reg = ctx.alloc_reg(); ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg))); let const_flag = if ctx.lookup_const_flag(name) { 1 } else { 0 }; ctx.inst(Inst::new( OpCode::STORE_UPVALUE, Operand::Imm(const_flag), Operand::Reg(new_reg), Operand::Imm(uv as u16), )); self.emit_module_write_through(name, new_reg, ctx)?; Ok(if update.prefix { new_reg } else { old_reg }) } else if let Some(cell_idx) = captured_cell { // 被捕获 cell:CELL_GET 旧值 + 常量 1 + ADD/SUB + CELL_SET。 // 后缀形式须保留旧值(结果寄存器),前缀返回新值。 let old_reg = ctx.alloc_reg(); if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) { ctx.inst(Inst::new( OpCode::CELL_GET, Operand::Reg(old_reg), Operand::Reg(binding.reg), Operand::Imm(cell_idx as u16), )); } else { ctx.inst(Inst::new( OpCode::CELL_GET, Operand::Reg(old_reg), Operand::None, Operand::Imm(cell_idx as u16), )); } let one_idx = ctx.add_constant(Constant::Int(1)); let one_reg = ctx.alloc_reg(); ctx.inst(Inst::load_const(Operand::Reg(one_reg), one_idx)); let op = if update.operator == UpdateOperator::Increment { OpCode::ADD } else { OpCode::SUB }; let new_reg = ctx.alloc_reg(); ctx.inst(Inst::new(op, Operand::Reg(new_reg), Operand::Reg(old_reg), Operand::Reg(one_reg))); ctx.inst(Inst::new( OpCode::CELL_SET, Operand::None, Operand::Reg(new_reg), Operand::Imm(cell_idx as u16), )); self.emit_module_write_through(name, new_reg, ctx)?; Ok(if update.prefix { new_reg } else { old_reg }) } else { let var_reg = ctx.lookup_or_global(name); if ctx.targets_readonly_builtin(name, var_reg) { // 全局不可写内置槽:strict 抛 TypeError(put 失败);sloppy 值照算 // (前缀返回新值、后缀返回旧值),跳过槽写。 if ctx.is_strict { return self.emit_throw_error("TypeError", "cannot assign to read-only property", ctx); } let tmp_reg = ctx.alloc_reg(); ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(tmp_reg), Operand::Reg(var_reg), Operand::None)); let result_reg = ctx.alloc_reg(); let op = match (update.operator, update.prefix) { (UpdateOperator::Increment, true) => OpCode::INC_PRE, (UpdateOperator::Increment, false) => OpCode::INC_POST, (UpdateOperator::Decrement, true) => OpCode::DEC_PRE, (UpdateOperator::Decrement, false) => OpCode::DEC_POST, }; ctx.inst(Inst::new(op, Operand::Reg(tmp_reg), Operand::Reg(result_reg), Operand::Reg(result_reg))); return Ok(result_reg); } let is_tier = self.is_global_tier_name(ctx, name); let is_implicit = ctx.is_implicit_global_reg(var_reg); if is_implicit && ctx.is_strict { // 严格模式未声明更新写:值无关抛 ReferenceError。 return self.emit_strict_undeclared_write(name, ctx); } // RMW 前从全局对象属性取旧值:tier 名(顶层已声明 var)与未声明名同, // 属性缺失按 undefined(update 旧值角落,GetBaseValue 语义,不抛)。 if is_tier || is_implicit { let key_idx = ctx.add_constant(Constant::String(name.to_string())); ctx.inst(Inst::new( OpCode::LOAD_GLOBAL_TYPEOF, Operand::Reg(var_reg), Operand::Const(key_idx), Operand::None, )); } let result_reg = ctx.alloc_reg(); let op = match (update.operator, update.prefix) { (UpdateOperator::Increment, true) => OpCode::INC_PRE, (UpdateOperator::Increment, false) => OpCode::INC_POST, (UpdateOperator::Decrement, true) => OpCode::DEC_PRE, (UpdateOperator::Decrement, false) => OpCode::DEC_POST, }; ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(result_reg), Operand::Reg(result_reg))); if is_tier { // 顶层已声明 var 自增/自减:新值写入全局对象属性(顶层 var 的唯一存储, // 引擎侧不保留镜像副本)。 self.emit_tier_global_write(name, var_reg, ctx); } else if is_implicit { self.emit_implicit_global_write(name, var_reg, ctx); } else if ctx.targets_writable_builtin(name, var_reg) { // 可写内置名:RMW 新值同步落全局对象属性。 self.emit_global_put_write(name, var_reg, ctx); } // 槽位寄存器 rd 承载更新后的新值:后缀形式 result_reg 保留旧值, // 写穿必须取 var_reg,否则命名空间条目被写回旧值。 self.emit_module_write_through(name, var_reg, ctx)?; Ok(result_reg) } } pub(crate) fn emit_operator(&self, expr: &Expression, ctx: &mut CompileCtx) -> Result { match expr { Expression::BinaryExpression(bin) => self.emit_binary_expression(bin, ctx), Expression::PrivateInExpression(pin) => self.emit_private_in_expression(pin, ctx), Expression::UnaryExpression(un) => self.emit_unary_expression(un, ctx), Expression::ConditionalExpression(cond) => self.emit_conditional_expression(cond, ctx), Expression::LogicalExpression(log) => self.emit_logical_expression(log, ctx), Expression::UpdateExpression(update) => self.emit_update_expression(update, ctx), _ => self.emit_unsupported_expression(expr, ctx), } } } /// CONCAT_N 摊平的操作数上限:CONCAT_N 单点读全部操作数(须同时存活),寄存器占用 /// = 操作数 + 结果 ≤ 15;超过上限回退左结合 ADD(超长链保持寄存器随链复用属性)。 const MAX_CONCAT_N_OPERANDS: usize = 14; /// 收集 Addition 左结合链的操作数(源码求值序):只下钻 left 链,right 恒为单个 /// 操作数(即使自身是 Addition 也不拆——括号右结合保持 f64 结合性不引入偏差)。 fn collect_add_operands<'expr, 'r>(expr: &'r Expression<'expr>, out: &mut Vec<&'r Expression<'expr>>) { if let Expression::BinaryExpression(bin) = expr { if bin.operator == BinaryOperator::Addition { collect_add_operands(&bin.left, out); out.push(&bin.right); return; } } out.push(expr); }