423 lines
22 KiB
Rust
423 lines
22 KiB
Rust
//! 赋值表达式 emit:简单 `=`、复合赋值与解构赋值(数组/对象 pattern)。
|
||
//! 函数:`emit_assignment_expression`。
|
||
|
||
use crate::{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::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> {
|
||
// 循环 update 段:被捕获绑定走寄存器 RMW(C 风格 for 每迭代 fresh,
|
||
// update 写寄存器供下一迭代 fresh 拷贝,不污染本迭代闭包捕获的 cell)。
|
||
if ctx.register_update_names.iter().any(|n| n == name) {
|
||
if let Some(reg) = ctx.scopes.symbols.lookup_any(name) {
|
||
ctx.inst(Inst::new(op, Operand::Reg(reg), Operand::Reg(rhs), Operand::None));
|
||
return Ok(reg);
|
||
}
|
||
}
|
||
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> {
|
||
if let oxide_parser::AssignmentTarget::StaticMemberExpression(member) = &assign.left {
|
||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||
let store_label = ctx.next_label_id();
|
||
let end_label = ctx.next_label_id();
|
||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||
let prop_name = member.property.name.as_str();
|
||
let 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), idx));
|
||
let result_reg = ctx.alloc_reg();
|
||
ctx.inst(Inst::new(
|
||
OpCode::LOAD_VAR,
|
||
Operand::Reg(result_reg),
|
||
Operand::Reg(obj_reg),
|
||
Operand::None,
|
||
));
|
||
ctx.inst(Inst::ic_get(Operand::Reg(result_reg), Operand::Reg(key_reg)));
|
||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||
ctx.inst(Inst::ic_set(Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||
ctx.inst(Inst::new(
|
||
OpCode::LOAD_VAR,
|
||
Operand::Reg(result_reg),
|
||
Operand::Reg(val_reg),
|
||
Operand::None,
|
||
));
|
||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||
return Ok(result_reg);
|
||
}
|
||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||
let prop_name = member.property.name.as_str();
|
||
let 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), idx));
|
||
if assign.operator != AssignmentOperator::Assign {
|
||
let obj = Operand::Reg(obj_reg);
|
||
let val = Operand::Reg(val_reg);
|
||
let key = Operand::Reg(key_reg);
|
||
match assign.operator {
|
||
AssignmentOperator::Addition => ctx.inst(Inst::compound_member_add(obj, val, key)),
|
||
AssignmentOperator::Subtraction => ctx.inst(Inst::compound_member_sub(obj, val, key)),
|
||
AssignmentOperator::Multiplication => ctx.inst(Inst::compound_member_mul(obj, val, key)),
|
||
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)
|
||
} else {
|
||
ctx.inst(Inst::ic_set(Operand::Reg(obj_reg), Operand::Reg(val_reg), Operand::Reg(key_reg)));
|
||
Ok(val_reg)
|
||
}
|
||
} else if let oxide_parser::AssignmentTarget::ComputedMemberExpression(member) = &assign.left {
|
||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||
let store_label = ctx.next_label_id();
|
||
let end_label = ctx.next_label_id();
|
||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||
let key_reg = self.emit_expression(&member.expression, ctx)?;
|
||
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),
|
||
));
|
||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||
ctx.inst(Inst::new(
|
||
OpCode::SET_PROP_DYNAMIC,
|
||
Operand::Reg(obj_reg),
|
||
Operand::Reg(key_reg),
|
||
Operand::Reg(val_reg),
|
||
));
|
||
ctx.inst(Inst::new(
|
||
OpCode::LOAD_VAR,
|
||
Operand::Reg(result_reg),
|
||
Operand::Reg(val_reg),
|
||
Operand::None,
|
||
));
|
||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||
return Ok(result_reg);
|
||
}
|
||
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,
|
||
Operand::Reg(obj_reg),
|
||
Operand::Reg(key_reg),
|
||
Operand::Reg(val_reg),
|
||
));
|
||
Ok(val_reg)
|
||
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
|
||
if assign.operator != AssignmentOperator::Assign {
|
||
return Err("compound assignment to private fields not supported".into());
|
||
}
|
||
let obj_reg = self.emit_expression(&member.object, ctx)?;
|
||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||
let name = member.field.name.as_str();
|
||
let (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
|
||
let key_reg = self.emit_private_id_reg(name, ctx)?;
|
||
ctx.inst(Inst::set_private(
|
||
Operand::Reg(obj_reg),
|
||
Operand::Reg(val_reg),
|
||
Operand::Reg(key_reg),
|
||
brand_reg,
|
||
brand_id,
|
||
));
|
||
Ok(val_reg)
|
||
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = &assign.left {
|
||
if assign.operator != AssignmentOperator::Assign {
|
||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||
let store_label = ctx.next_label_id();
|
||
let end_label = ctx.next_label_id();
|
||
let name = id_ref.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();
|
||
let result_reg = ctx.alloc_reg();
|
||
if let Some(uv) = uv_idx {
|
||
ctx.inst(Inst::new(
|
||
OpCode::LOAD_UPVALUE,
|
||
Operand::Reg(result_reg),
|
||
Operand::Imm(uv as u16),
|
||
Operand::None,
|
||
));
|
||
} else if let Some(cell_idx) = captured_cell {
|
||
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(result_reg),
|
||
a_operand,
|
||
Operand::Imm(cell_idx as u16),
|
||
));
|
||
} else {
|
||
let var_reg = ctx.lookup_or_global(name);
|
||
ctx.inst(Inst::new(
|
||
OpCode::LOAD_VAR,
|
||
Operand::Reg(result_reg),
|
||
Operand::Reg(var_reg),
|
||
Operand::None,
|
||
));
|
||
}
|
||
self.emit_logical_assign_test(logical_op, result_reg, store_label, end_label, ctx)?;
|
||
ctx.labels.set_label_pos(store_label, ctx.insts.len());
|
||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||
if let Some(uv) = uv_idx {
|
||
ctx.inst(Inst::new(
|
||
OpCode::STORE_UPVALUE,
|
||
Operand::None,
|
||
Operand::Reg(val_reg),
|
||
Operand::Imm(uv as u16),
|
||
));
|
||
} else if let Some(cell_idx) = captured_cell {
|
||
ctx.inst(Inst::new(
|
||
OpCode::CELL_SET,
|
||
Operand::None,
|
||
Operand::Reg(val_reg),
|
||
Operand::Imm(cell_idx as u16),
|
||
));
|
||
} else {
|
||
let is_const = ctx.lookup_const_flag(name);
|
||
let const_flag = if is_const { 1 } else { 0 };
|
||
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),
|
||
));
|
||
}
|
||
ctx.inst(Inst::new(
|
||
OpCode::LOAD_VAR,
|
||
Operand::Reg(result_reg),
|
||
Operand::Reg(val_reg),
|
||
Operand::None,
|
||
));
|
||
ctx.labels.set_label_pos(end_label, ctx.insts.len());
|
||
Ok(result_reg)
|
||
} else if assign.operator == AssignmentOperator::Addition
|
||
|| assign.operator == AssignmentOperator::Subtraction
|
||
|| assign.operator == AssignmentOperator::Multiplication
|
||
|| assign.operator == AssignmentOperator::Division
|
||
|| assign.operator == AssignmentOperator::Remainder
|
||
|| assign.operator == AssignmentOperator::Exponential
|
||
|| assign.operator == AssignmentOperator::BitwiseAnd
|
||
|| assign.operator == AssignmentOperator::BitwiseOR
|
||
|| assign.operator == AssignmentOperator::BitwiseXOR
|
||
|| assign.operator == AssignmentOperator::ShiftLeft
|
||
|| assign.operator == AssignmentOperator::ShiftRight
|
||
|| assign.operator == AssignmentOperator::ShiftRightZeroFill
|
||
{
|
||
let rhs = self.emit_expression(&assign.right, ctx)?;
|
||
let name = id_ref.name.as_str();
|
||
let op = match assign.operator {
|
||
AssignmentOperator::Addition => OpCode::COMPOUND_ADD,
|
||
AssignmentOperator::Subtraction => OpCode::COMPOUND_SUB,
|
||
AssignmentOperator::Multiplication => OpCode::COMPOUND_MUL,
|
||
AssignmentOperator::Division => OpCode::COMPOUND_DIV,
|
||
AssignmentOperator::Remainder => OpCode::COMPOUND_MOD,
|
||
AssignmentOperator::Exponential => OpCode::COMPOUND_EXP,
|
||
AssignmentOperator::BitwiseAnd => OpCode::COMPOUND_AND,
|
||
AssignmentOperator::BitwiseOR => OpCode::COMPOUND_OR,
|
||
AssignmentOperator::BitwiseXOR => OpCode::COMPOUND_XOR,
|
||
AssignmentOperator::ShiftLeft => OpCode::COMPOUND_SHL,
|
||
AssignmentOperator::ShiftRight => OpCode::COMPOUND_SHR,
|
||
AssignmentOperator::ShiftRightZeroFill => OpCode::COMPOUND_USHR,
|
||
_ => return Err(format!("compound assignment operator {:?} not supported", assign.operator)),
|
||
};
|
||
// 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::GET_PROP_DYNAMIC,
|
||
Operand::Reg(obj_reg),
|
||
Operand::Reg(key_reg),
|
||
Operand::Reg(val_reg),
|
||
));
|
||
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
|
||
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));
|
||
|
||
// 对象无该属性:回退静态复合赋值,结果写入同一 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::LOAD_VAR,
|
||
Operand::Reg(val_reg),
|
||
Operand::Reg(fallback_val),
|
||
Operand::None,
|
||
));
|
||
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))
|
||
}
|
||
} else {
|
||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||
let name = id_ref.name.as_str();
|
||
// 赋值函数/箭头/class 表达式 → 推断 name。
|
||
if crate::is_anonymous_function_definition(&assign.right) {
|
||
if let Some(sub_mod) = ctx.nested.last_mut() {
|
||
if sub_mod.function_name.is_none() {
|
||
sub_mod.function_name = Some(name.to_string());
|
||
}
|
||
}
|
||
}
|
||
// 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);
|
||
}
|
||
let is_const = ctx.lookup_const_flag(name);
|
||
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!(
|
||
&assign.left,
|
||
oxide_parser::AssignmentTarget::ArrayAssignmentTarget(_)
|
||
| oxide_parser::AssignmentTarget::ObjectAssignmentTarget(_)
|
||
) {
|
||
if assign.operator != AssignmentOperator::Assign {
|
||
return Err("compound destructuring assignment not supported".into());
|
||
}
|
||
let val_reg = self.emit_expression(&assign.right, ctx)?;
|
||
self.emit_assign_target(&assign.left, val_reg, ctx)?;
|
||
Ok(val_reg)
|
||
} else {
|
||
Err("assignment target not supported".into())
|
||
}
|
||
}
|
||
}
|