feat: with 语句、成员位/移位复合赋值、CALL_NATIVE 无目标修复

with 语句:新增 emit 支持,with 体内自由标识符动态解析(对象属性优先,
回退外层),支持读/写/复合赋值/自增自减/delete/var 初始化动态化,
TO_OBJECT opcode 处理原始值对象,顶层 this 绑定全局对象。

复合赋值:新增 6 个 COMPOUND_MEMBER_BIT_* opcode 支持成员位/移位复合赋值,
修复 computed member 复合赋值覆盖 bug,with 内标识符复合赋值动态化。

CALL_NATIVE:实现全局 isNaN/isFinite 并绑定,用户覆盖 builtin 名时
回退通用调用路径而非报错,局部遮蔽 builtin 名走普通 CALL。

test262: with 34 pass(基线 0),compound-assignment 320 pass(基线 223)
This commit is contained in:
2026-08-09 07:22:46 +08:00
parent 826d9fd0a2
commit 5611d90ad0
23 changed files with 829 additions and 137 deletions
+137 -58
View File
@@ -9,6 +9,60 @@ use oxide_ir::operand::Operand;
use oxide_parser::AssignmentOperator;
impl Emitter {
/// 标识符复合赋值的静态路径:upvalue / 被捕获 cell 走显式读取-运算-写回
/// (写穿透共享单元),普通槽用 COMPOUND_* 寄存器内 RMW。返回结果寄存器。
///
/// # 边界与前提
/// - `op` 必须是 COMPOUND_* 族(rd 兼作 lhs 源,a 为 rhs)。
/// - upvalue / cell 场景忽略 const 语义(共享单元由闭包机制保证)。
fn emit_compound_identifier_static(
&self, name: &str, op: OpCode, rhs: u32, ctx: &mut CompileCtx,
) -> Result<u32, String> {
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let captured_cell = ctx.captured_bindings.get(name).copied();
if let Some(uv) = uv_idx {
let val_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
Operand::Reg(val_reg),
Operand::Imm(uv as u16),
Operand::None,
));
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
ctx.inst(Inst::new(
OpCode::STORE_UPVALUE,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(uv as u16),
));
Ok(val_reg)
} else if let Some(cell_idx) = captured_cell {
let val_reg = ctx.alloc_reg();
let a_operand = match ctx.scopes.symbols.lookup_any_binding(name) {
Some((binding, _)) => Operand::Reg(binding.reg),
None => Operand::None,
};
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(val_reg),
a_operand,
Operand::Imm(cell_idx as u16),
));
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
ctx.inst(Inst::new(
OpCode::CELL_SET,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(cell_idx as u16),
));
Ok(val_reg)
} else {
let var_reg = ctx.lookup_or_global(name);
ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(rhs), Operand::None));
Ok(var_reg)
}
}
pub(crate) fn emit_assignment_expression(
&self, assign: &oxide_parser::AssignmentExpression, ctx: &mut CompileCtx,
) -> Result<u32, String> {
@@ -59,6 +113,12 @@ impl Emitter {
AssignmentOperator::Division => ctx.inst(Inst::compound_member_div(obj, val, key)),
AssignmentOperator::Remainder => ctx.inst(Inst::compound_member_mod(obj, val, key)),
AssignmentOperator::Exponential => ctx.inst(Inst::compound_member_exp(obj, val, key)),
AssignmentOperator::BitwiseAnd => ctx.inst(Inst::compound_member_bit_and(obj, val, key)),
AssignmentOperator::BitwiseOR => ctx.inst(Inst::compound_member_bit_or(obj, val, key)),
AssignmentOperator::BitwiseXOR => ctx.inst(Inst::compound_member_bit_xor(obj, val, key)),
AssignmentOperator::ShiftLeft => ctx.inst(Inst::compound_member_shl(obj, val, key)),
AssignmentOperator::ShiftRight => ctx.inst(Inst::compound_member_shr(obj, val, key)),
AssignmentOperator::ShiftRightZeroFill => ctx.inst(Inst::compound_member_ushr(obj, val, key)),
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
}
Ok(val_reg)
@@ -99,6 +159,40 @@ impl Emitter {
}
let obj_reg = self.emit_expression(&member.object, ctx)?;
let key_reg = self.emit_expression(&member.expression, ctx)?;
if assign.operator != AssignmentOperator::Assign {
// 复合赋值:读属性 → 运算 → 写回。val_reg 同时承载旧值与新值。
let val_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(val_reg),
));
let rhs = self.emit_expression(&assign.right, ctx)?;
let op = match assign.operator {
AssignmentOperator::Addition => OpCode::ADD,
AssignmentOperator::Subtraction => OpCode::SUB,
AssignmentOperator::Multiplication => OpCode::MUL,
AssignmentOperator::Division => OpCode::DIV,
AssignmentOperator::Remainder => OpCode::MOD,
AssignmentOperator::Exponential => OpCode::COMPOUND_EXP,
AssignmentOperator::BitwiseAnd => OpCode::BIT_AND,
AssignmentOperator::BitwiseOR => OpCode::BIT_OR,
AssignmentOperator::BitwiseXOR => OpCode::BIT_XOR,
AssignmentOperator::ShiftLeft => OpCode::SHL,
AssignmentOperator::ShiftRight => OpCode::SHR,
AssignmentOperator::ShiftRightZeroFill => OpCode::USHR,
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
};
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(rhs)));
ctx.inst(Inst::new(
OpCode::SET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(val_reg),
));
return Ok(val_reg);
}
let val_reg = self.emit_expression(&assign.right, ctx)?;
ctx.inst(Inst::new(
OpCode::SET_PROP_DYNAMIC,
@@ -184,51 +278,55 @@ impl Emitter {
AssignmentOperator::ShiftRightZeroFill => OpCode::COMPOUND_USHR,
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
};
// 目标为 upvalue / 被捕获 cell 时走显式读取-运算-写回,保证写穿透共享单元;
// 只有普通槽位才用 COMPOUND_* 的寄存器内 RMW(rd 兼作 lhs 源)。
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let captured_cell = ctx.captured_bindings.get(name).copied();
if let Some(uv) = uv_idx {
// 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 val_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
OpCode::GET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(val_reg),
Operand::Imm(uv as u16),
Operand::None,
));
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
ctx.inst(Inst::new(
OpCode::STORE_UPVALUE,
Operand::None,
OpCode::SET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(val_reg),
Operand::Imm(uv as u16),
));
Ok(val_reg)
} else if let Some(cell_idx) = captured_cell {
let val_reg = ctx.alloc_reg();
let a_operand = match ctx.scopes.symbols.lookup_any_binding(name) {
Some((binding, _)) => Operand::Reg(binding.reg),
None => Operand::None,
};
ctx.inst(Inst::jmp(end_label));
// 对象无该属性:回退静态复合赋值,结果写入同一 val_reg。
ctx.labels.set_label_pos(fallback_label, ctx.insts.len());
let fallback_val = self.emit_compound_identifier_static(name, op, rhs, ctx)?;
ctx.inst(Inst::new(
OpCode::CELL_GET,
OpCode::LOAD_VAR,
Operand::Reg(val_reg),
a_operand,
Operand::Imm(cell_idx as u16),
));
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
ctx.inst(Inst::new(
OpCode::CELL_SET,
Operand::Reg(fallback_val),
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(cell_idx as u16),
));
Ok(val_reg)
} else {
let var_reg = ctx.lookup_or_global(name);
ctx.inst(Inst::new(op, Operand::Reg(var_reg), Operand::Reg(rhs), Operand::None));
Ok(var_reg)
ctx.labels.set_label_pos(end_label, ctx.insts.len());
return Ok(val_reg);
}
self.emit_compound_identifier_static(name, op, rhs, ctx)
} else {
Err(format!("compound assignment operator {:?} not supported", assign.operator))
}
@@ -243,35 +341,16 @@ impl Emitter {
}
}
}
// 目标若是 upvalue 引用,走 STORE_UPVALUE
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
ctx.inst(Inst::new(
OpCode::STORE_UPVALUE,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(uv_idx as u16),
));
// with 体内的自由标识符走动态写:对象有属性则写对象,否则写外层。
if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) {
let is_const = ctx.lookup_const_flag(name);
let const_flag: u16 = if is_const { 1 } else { 0 };
self.emit_with_dynamic_write(name, val_reg, const_flag, ctx);
return Ok(val_reg);
}
// 目标若是被捕获 cell,走 CELL_SET
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
ctx.inst(Inst::new(
OpCode::CELL_SET,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(cell_idx as u16),
));
return Ok(val_reg);
}
let var_reg = ctx.lookup_or_global(name);
let is_const = ctx.lookup_const_flag(name);
let const_flag = if is_const { 1 } else { 0 };
ctx.inst(Inst::new(
OpCode::STORE_VAR,
Operand::Reg(var_reg),
Operand::Reg(val_reg),
Operand::Imm(const_flag),
));
let const_flag: u16 = if is_const { 1 } else { 0 };
self.emit_identifier_store(name, val_reg, const_flag, ctx);
Ok(val_reg)
}
} else if matches!(
+6 -1
View File
@@ -112,7 +112,12 @@ impl Emitter {
let mut static_regs = words;
let first_arg_reg = if static_regs.is_empty() { 0u32 } else { pack_arg_regs(&mut static_regs, ctx) };
let op = match &call.callee {
Expression::Identifier(ident) if ctx.is_builtin(ident.name.as_str()) => OpCode::CALL_NATIVE,
// 仅未遮蔽的内置名才用 CALL_NATIVE;局部声明覆盖后走通用 CALL。
Expression::Identifier(ident)
if ctx.is_builtin(ident.name.as_str()) && !ctx.is_local_shadowing_builtin(ident.name.as_str()) =>
{
OpCode::CALL_NATIVE
}
_ => OpCode::CALL,
};
match op {
+169 -6
View File
@@ -1,16 +1,17 @@
//! 标识符表达式 emit:`emit_identifier_expression` 按绑定/builtin/全局解析寄存器。
//! 标识符表达式 emit:按绑定/builtin/全局解析寄存器。
//!
//! with 语句体内,自由标识符(非 with 内部声明的绑定)编译为动态解析:
//! 运行时先查 with 对象是否有该属性,命中则取属性值,否则回退静态解析。
use crate::{CompileCtx, Emitter};
use oxide_bytecode::module::Constant;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
impl Emitter {
pub(crate) fn emit_identifier_expression(
&self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx,
) -> Result<u32, String> {
let name = ident.name.as_str();
/// 静态标识符读取:upvalue / 被捕获 cell / 全局槽。
fn emit_static_identifier_read(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
for (uv_idx, up) in ctx.current_upvalue_captures.iter().enumerate() {
if up.name == name {
let r = ctx.alloc_reg();
@@ -40,4 +41,166 @@ impl Emitter {
ctx.inst(Inst::new(OpCode::LOAD_VAR, Operand::Reg(r), Operand::Reg(var_reg), Operand::None));
Ok(r)
}
/// with 内动态读取:先查 with 对象属性,无则回退静态解析。
///
/// # 边界与前提
/// - 仅在 `with_stack` 非空且名字非 with 内部绑定时调用。
/// - 对象属性存在性用 `in` 判定(含原型链),与对象环境记录的 HasBinding 一致。
fn emit_with_dynamic_read(&self, name: &str, ctx: &mut CompileCtx) -> Result<u32, String> {
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 result_reg = ctx.alloc_reg();
ctx.inst(Inst::new(
OpCode::GET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(result_reg),
));
ctx.inst(Inst::jmp(end_label));
ctx.labels.set_label_pos(fallback_label, ctx.insts.len());
// 回退:upvalue / 被捕获 cell / 静态绑定 / 未定义四选一。
// 对象无该属性时才走此处,语义与静态解析一致。
if let Some((uv_idx, _)) = ctx.current_upvalue_captures.iter().enumerate().find(|(_, u)| u.name == name) {
ctx.inst(Inst::new(
OpCode::LOAD_UPVALUE,
Operand::Reg(result_reg),
Operand::Imm(uv_idx as u16),
Operand::None,
));
} else if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(result_reg),
Operand::Reg(binding.reg),
Operand::Imm(cell_idx as u16),
));
} else {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(result_reg),
Operand::None,
Operand::Imm(cell_idx as u16),
));
}
} else if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
ctx.inst(Inst::new(
OpCode::LOAD_VAR,
Operand::Reg(result_reg),
Operand::Reg(reg),
Operand::None,
));
} else {
let undef_idx = ctx.add_constant(Constant::Undefined);
ctx.inst(Inst::load_const(Operand::Reg(result_reg), undef_idx));
}
ctx.labels.set_label_pos(end_label, ctx.insts.len());
Ok(result_reg)
}
/// 静态标识符写入:upvalue / 被捕获 cell / 普通槽(含 const guard)。
///
/// # 边界与前提
/// - `const_flag` 为 1 时走 STORE_VAR 运行时 const guard(对 const 再赋值报错)。
/// - upvalue 与 cell 写穿共享单元,不设 guard。
pub(crate) fn emit_identifier_store(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
// 目标若是 upvalue 引用,走 STORE_UPVALUE
if let Some(uv_idx) = ctx.current_upvalue_captures.iter().position(|u| u.name == name) {
ctx.inst(Inst::new(
OpCode::STORE_UPVALUE,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(uv_idx as u16),
));
return;
}
// 目标若是被捕获 cell,走 CELL_SET
if let Some(&cell_idx) = ctx.captured_bindings.get(name) {
ctx.inst(Inst::new(
OpCode::CELL_SET,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(cell_idx as u16),
));
return;
}
let var_reg = ctx.lookup_or_global(name);
ctx.inst(Inst::new(
OpCode::STORE_VAR,
Operand::Reg(var_reg),
Operand::Reg(val_reg),
Operand::Imm(const_flag),
));
}
/// with 内动态写入:对象有该属性则写对象,否则回退静态写入。
///
/// # 边界与前提
/// - 仅在 `with_stack` 非空且名字非 with 内部绑定时调用。
/// - 对象属性存在性判定与动态读取一致(`in` 含原型链)。
/// - 回退目标未在静态作用域声明时不登记全局(with 外不应可见)。
pub(crate) fn emit_with_dynamic_write(&self, name: &str, val_reg: u32, const_flag: u16, ctx: &mut CompileCtx) {
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));
ctx.inst(Inst::new(
OpCode::SET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
Operand::Reg(val_reg),
));
ctx.inst(Inst::jmp(end_label));
ctx.labels.set_label_pos(fallback_label, ctx.insts.len());
// 回退只写静态作用域已声明的绑定;未声明时丢弃值(隐式全局在 with 外不可解析)。
if ctx.scopes.symbols.lookup_any(name).is_some() {
self.emit_identifier_store(name, val_reg, const_flag, ctx);
}
ctx.labels.set_label_pos(end_label, ctx.insts.len());
}
pub(crate) fn emit_identifier_expression(
&self, ident: &oxide_parser::IdentifierReference, ctx: &mut CompileCtx,
) -> Result<u32, String> {
let name = ident.name.as_str();
// with 体内的自由标识符走动态解析(对象属性优先,回退外层)。
if !ctx.with_stack.is_empty() && !ctx.is_with_internal_binding(name) {
return self.emit_with_dynamic_read(name, ctx);
}
self.emit_static_identifier_read(name, ctx)
}
}
+182 -60
View File
@@ -61,7 +61,54 @@ impl Emitter {
fn emit_unary_expression(&self, un: &oxide_parser::UnaryExpression, ctx: &mut CompileCtx) -> Result<u32, String> {
if matches!(un.operator, UnaryOperator::Delete) {
return match &un.argument {
Expression::Identifier(_) => {
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);
}
// 严格模式的 delete 标识符由 oxc_semantic 提前拦截为早期错误;
// 非严格语义返回 false(标识符不可删除)。
let idx = ctx.add_constant(Constant::Boolean(false));
@@ -262,54 +309,32 @@ impl Emitter {
match &update.argument {
SimpleAssignmentTarget::AssignmentTargetIdentifier(id) => {
let name = id.name.as_str();
// 判断目标是 upvalue 还是被捕获 cell
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let captured_cell = ctx.captured_bindings.get(name).copied();
if let Some(uv) = uv_idx {
// upvalue:LOAD_UPVALUE + 常量 1 + ADD/SUB + STORE_UPVALUE;
// 后缀形式返回旧值(前缀返回新值)。
// 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::LOAD_UPVALUE,
OpCode::GET_PROP_DYNAMIC,
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
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)));
ctx.inst(Inst::new(
OpCode::STORE_UPVALUE,
Operand::None,
Operand::Reg(new_reg),
Operand::Imm(uv as u16),
));
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 val_reg = ctx.alloc_reg();
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(val_reg),
Operand::Reg(binding.reg),
Operand::Imm(cell_idx as u16),
));
} else {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(val_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));
@@ -318,26 +343,41 @@ impl Emitter {
} else {
OpCode::SUB
};
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg)));
ctx.inst(Inst::new(
OpCode::CELL_SET,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(cell_idx as u16),
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),
));
Ok(val_reg)
} else {
let var_reg = ctx.lookup_or_global(name);
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)));
Ok(result_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)?;
@@ -376,6 +416,88 @@ impl Emitter {
}
}
/// 标识符自增/自减的静态路径:upvalue / 被捕获 cell 走显式读-增减-写回,
/// 普通槽用 INC_PRE/POST 或 DEC_PRE/POST。返回结果寄存器。
fn emit_identifier_update_static(
&self, name: &str, update: &oxide_parser::UpdateExpression, ctx: &mut CompileCtx,
) -> Result<u32, String> {
let uv_idx = ctx.current_upvalue_captures.iter().position(|u| u.name == name);
let captured_cell = ctx.captured_bindings.get(name).copied();
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)));
ctx.inst(Inst::new(
OpCode::STORE_UPVALUE,
Operand::None,
Operand::Reg(new_reg),
Operand::Imm(uv as u16),
));
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 val_reg = ctx.alloc_reg();
if let Some((binding, _)) = ctx.scopes.symbols.lookup_any_binding(name) {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(val_reg),
Operand::Reg(binding.reg),
Operand::Imm(cell_idx as u16),
));
} else {
ctx.inst(Inst::new(
OpCode::CELL_GET,
Operand::Reg(val_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
};
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(one_reg)));
ctx.inst(Inst::new(
OpCode::CELL_SET,
Operand::None,
Operand::Reg(val_reg),
Operand::Imm(cell_idx as u16),
));
Ok(val_reg)
} else {
let var_reg = ctx.lookup_or_global(name);
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)));
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),