Files
OxideJS-New/crates/oxide_emit/src/expr/operator/mod.rs
T

1092 lines
58 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 运算符域:二元/一元/条件/逻辑/更新/`in` 表达式 emit 与分发。
//! 逻辑与 `??` 用短路跳转,复合求值利用 `is_side_effect_free` 优化。
//! 函数:`emit_operator` 及各类 `emit_*_expression`。
pub(crate) mod fold;
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<u32, String> {
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<u32, String> {
// 双常量折叠:两操作数均为字面量且通过语义守卫时,编译期折单常量,
// 发射单条 LOAD_CONST(编码镜像运行时指令产物,见 fold.rs)。
// 置于 CONCAT_N 摊平之前,命中即提前返回。
if let (Some(l), Some(r)) = (fold::literal_constant(&bin.left), fold::literal_constant(&bin.right)) {
if let Some(folded) = fold::fold_binary(bin.operator, &l, &r) {
let idx = ctx.add_constant(folded);
let r = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
return Ok(r);
}
}
// 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);
// 字符串域链折叠:全操作数为可折叠字面量且至少一个字符串字面量时,
// 编译期折单字符串常量(单元口径见 fold.rs),发射单条 LOAD_CONST。
if let Some(folded) = fold::fold_string_chain(&operands) {
let idx = ctx.add_constant(folded);
let r = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
return Ok(r);
}
// 上限防寄存器压力: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), &regs));
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<u32, String> {
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);
}
// 一元常量折叠:操作数为字面量且通过语义守卫时折单常量,发射单条
// LOAD_CONST(编码镜像运行时指令产物,见 fold.rs)。
if let Some(c) = fold::literal_constant_wide(&un.argument) {
if let Some(folded) = fold::fold_unary(un.operator, &c) {
let idx = ctx.add_constant(folded);
let r = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
return Ok(r);
}
}
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<u32, String> {
match operand {
Expression::Identifier(ident) => {
let name = ident.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));
// 复制 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());
// with 对象无该属性:引用解析到外层绑定,按外层绑定的可删除性
// 定值(声明式绑定 → false;全局属性 → 运行期探针)。
let binding_result = self.emit_delete_binding_result(name, ctx)?;
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(result_reg),
Operand::Reg(binding_result),
Operand::None,
));
ctx.labels.set_label_pos(end_label, ctx.insts.len());
return Ok(result_reg);
}
self.emit_delete_binding_result(name, ctx)
}
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)
}
}
}
/// delete 标识符回退外层绑定的定值:可删全局内置镜像槽先行(真删加清槽),
/// 其余按 DeleteBinding 三分类发运行期探针(未声明名/隐式全局槽/eval 自身
/// 顶层 var/函数名)或 false 常数(局部绑定/脚本自身顶层 var/函数名/eval
/// 自身顶层 let/const)。返回结果寄存器。
///
/// # 步骤
/// 1. 可删全局内置镜像槽命中:发 DELETE_GLOBAL_PROP_C(真删加清槽)返结果寄存器
/// 2. DeleteBinding 三分类:局部绑定与脚本自身顶层已声明名发 false 常数;
/// 未声明名/隐式全局槽/eval 自身顶层 var/函数名发全局对象运行期探针
///
/// # 边界与前提
/// - 严格模式的 delete 标识符由语义分析提前拦截为早期错误,本臂在 strict 代码不可达
///
/// # 副作用
/// - 探针臂真删隐式全局槽后登记 `implicit_global_reads`(同程序后续裸读该名
/// 走全局对象属性,缺失抛 ReferenceError)
fn emit_delete_binding_result(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
// 可删全局内置(可写全局名除宿主名 $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 程序见
// 不到调用方域变量,静态"当前程序内是否声明"粗于规范动态判定,故
// 不可解析面一律运行期定值。
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
}
}
}
};
// 探针臂:全局对象运行期定值(缺失 → true;不可配置 → false 且保留;
// 可配置 → 真删且 true)。
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);
}
// 声明式绑定与脚本自身顶层已声明名:恒 false。
let idx = ctx.add_constant(Constant::Boolean(false));
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<u32, String> {
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);
// 已知 builtin 名(非局部遮蔽)同走属性路:delete 真删后缺失 →
// "undefined" 而非经镜像槽读 undefined 同值巧合。
let is_builtin_slot = ctx.is_builtin(name) && !ctx.is_local_shadowing_builtin(name);
if !in_with_dynamic && !captured && (is_tier || binding.is_none() || implicit_global || is_builtin_slot) {
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<u32, String> {
// 条件常量折叠:test 与两臂均为字面量时折单常量,发射单条 LOAD_CONST。
if let (Some(t), Some(c), Some(a)) = (
fold::literal_constant_wide(&cond.test),
fold::literal_constant_wide(&cond.consequent),
fold::literal_constant_wide(&cond.alternate),
) {
if let Some(folded) = fold::fold_conditional(&t, &c, &a) {
let idx = ctx.add_constant(folded);
let r = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
return Ok(r);
}
}
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<u32, String> {
// 逻辑常量折叠:两侧均为字面量时折单常量,发射单条 LOAD_CONST。
if let (Some(l), Some(r)) = (fold::literal_constant_wide(&log.left), fold::literal_constant_wide(&log.right)) {
if let Some(folded) = fold::fold_logical(log.operator, &l, &r) {
let idx = ctx.add_constant(folded);
let r = ctx.alloc_reg();
ctx.inst(Inst::load_const(Operand::Reg(r), idx));
return Ok(r);
}
}
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<u32, String> {
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) => {
// super 属性自增/自减:this 先装载(GetThisBinding),键常量装载,
// 读旧值(SUPER_GET_PROP),运算,写回(SUPER_PUT_PROP)。
if matches!(&member.object, Expression::Super(_)) {
let this_reg = self.emit_super_this(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 old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::SUPER_GET_PROP,
Operand::Reg(old_reg),
Operand::Reg(this_reg),
Operand::Reg(key_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 new_reg = ctx.alloc_reg();
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)));
self.emit_super_put(this_reg, key_reg, new_reg, ctx);
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,
));
return Ok(result_reg);
}
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) => {
// super 计算成员自增/自减:this 先装载(GetThisBinding),键表达式
// 后求值,读旧值(SUPER_GET_PROP),运算,写回(SUPER_PUT_PROP)。
if matches!(&member.object, Expression::Super(_)) {
let this_reg = self.emit_super_this(ctx)?;
let key_reg = self.emit_expression(&member.expression, ctx)?;
let old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::SUPER_GET_PROP,
Operand::Reg(old_reg),
Operand::Reg(this_reg),
Operand::Reg(key_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 new_reg = ctx.alloc_reg();
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)));
self.emit_super_put(this_reg, key_reg, new_reg, ctx);
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,
));
return Ok(result_reg);
}
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<u32, String> {
// 自增/自减先解析赋值引用: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)?;
// 函数名不可写绑定(sloppy):put 永不成功,值照算(前缀返回新值、后缀返回
// 旧值),跳过槽写;strict 已在 emit_const_write_guard 抛 TypeError(不可达)。
if ctx.lookup_non_writable_flag(name) {
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let old_reg = ctx.alloc_reg();
if let Some(uv) = uv_idx {
ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
Operand::Reg(old_reg),
Operand::Imm(uv as u16),
Operand::None,
));
} else if let Some((cell_idx, binding_reg)) = ctx.visible_cell(name) {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(old_reg),
Operand::Reg(binding_reg),
Operand::Imm(cell_idx as u16),
));
} else {
let var_reg = ctx.lookup_or_global(name);
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(old_reg), Operand::Reg(var_reg), 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)));
return Ok(if update.prefix { new_reg } else { old_reg });
}
}
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
// 循环 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 = if update.prefix {
// 前缀形:新值直接落临时寄存器,a 槽别名 rd 合并双写。
ctx.inst(Inst::new(op, Operand::Reg(tmp_reg), Operand::Reg(tmp_reg), Operand::None));
tmp_reg
} else {
let old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(old_reg),
Operand::Reg(tmp_reg),
Operand::None,
));
ctx.inst(Inst::new(op, Operand::Reg(old_reg), Operand::Reg(tmp_reg), Operand::None));
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::new(
OpCode::LOAD_VAR,
Operand::Reg(tmp_reg),
Operand::Reg(old_reg),
Operand::None,
));
result_reg
};
return Ok(result_reg);
}
// 后缀形:增减指令把转换后旧值写循环变量寄存器、新值写新鲜寄存器
//(新鲜寄存器活区间跨指令,别名写不冲毁活值),结果返旧值。
let result_reg = if update.prefix {
// 前缀形:新值直接落循环变量寄存器,a 槽别名 rd 合并双写。
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(reg), Operand::None));
reg
} else {
let old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(old_reg), Operand::Reg(reg), Operand::None));
ctx.inst(Inst::new(op, Operand::Reg(old_reg), Operand::Reg(reg), Operand::None));
let result_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(result_reg), Operand::Reg(reg), Operand::None));
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(reg), Operand::Reg(old_reg), Operand::None));
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_global_lex_mirror_write(name, new_reg, ctx);
self.emit_module_write_through(name, new_reg, ctx)?;
Ok(if update.prefix { new_reg } else { old_reg })
} else if let Some((cell_idx, binding_reg)) = ctx.visible_cell(name) {
// 被捕获 cell:CELL_GET 旧值 + 常量 1 + ADD/SUB + CELL_SET。
// 后缀形式须保留旧值(结果寄存器),前缀返回新值。
let old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(old_reg),
Operand::Reg(binding_reg),
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_global_lex_mirror_write(name, new_reg, ctx);
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 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,
};
// 后缀形:增减指令把转换后旧值写临时寄存器、新值写新鲜寄存器
//(新鲜寄存器活区间跨指令,别名写不冲毁活值),结果返旧值。
let result_reg = if update.prefix {
// 前缀形:新值直接落临时寄存器,a 槽别名 rd 合并双写。
ctx.inst(Inst::new(op, Operand::Reg(tmp_reg), Operand::Reg(tmp_reg), Operand::None));
tmp_reg
} else {
let old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(old_reg), Operand::Reg(tmp_reg), Operand::None));
ctx.inst(Inst::new(op, Operand::Reg(old_reg), Operand::Reg(tmp_reg), Operand::None));
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::new(OpCode::LOAD_VAR, Operand::Reg(tmp_reg), Operand::Reg(old_reg), Operand::None));
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);
let is_builtin = ctx.is_builtin(name) && !ctx.is_local_shadowing_builtin(name);
if is_implicit && ctx.is_strict {
// 严格模式未声明更新写:值无关抛 ReferenceError。
return self.emit_strict_undeclared_write(name, ctx);
}
// RMW 前从全局对象属性取旧值:tier 名(顶层已声明 var)与未声明名同,
// 属性缺失按 undefined(update 旧值角落,GetBaseValue 语义,不抛);
// 已知 builtin 名(非局部遮蔽)读 A 侧属性,属性缺失(delete 真删后)
// 抛 ReferenceError,与裸读同形——镜像槽不反映删除,无法自区分缺位
// 与在位 undefined。
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,
));
} else if is_builtin {
let key_idx = ctx.add_constant(Constant::String(name.to_string()));
ctx.inst(Inst::new(
OpCode::LOAD_GLOBAL,
Operand::Reg(var_reg),
Operand::Const(key_idx),
Operand::None,
));
}
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,
};
// 后缀形:增减指令把转换后旧值写槽寄存器(取数之后)、新值写新鲜寄存器
//(新鲜寄存器活区间跨指令,别名写不冲毁活值),结果返旧值。
let result_reg = if update.prefix {
// 前缀形:新值直接落槽寄存器,a 槽别名 rd 合并双写。
ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(var_reg), Operand::None));
var_reg
} else {
let old_reg = ctx.alloc_reg();
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(old_reg), Operand::Reg(var_reg), Operand::None));
ctx.inst(Inst::new(op, Operand::Reg(old_reg), Operand::Reg(var_reg), Operand::None));
let result_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(result_reg),
Operand::Reg(var_reg),
Operand::None,
));
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(var_reg), Operand::Reg(old_reg), Operand::None));
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);
}
// 顶层词法双写镜像:更新后新值同步写镜像,使跨脚本(间接 eval)读经
// 镜像见当前值。
self.emit_global_lex_mirror_write(name, var_reg, ctx);
// 槽位寄存器承载更新后的新值:写穿必须取 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<u32, String> {
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);
}