feat: 私有字段复合赋值 emit 支持(GET_PRIVATE→op→SET_PRIVATE 序列)

This commit is contained in:
2026-08-16 12:12:26 +08:00
parent 161c4c2c41
commit bbc09a8208
2 changed files with 260 additions and 12 deletions
+87 -12
View File
@@ -214,22 +214,97 @@ impl Emitter {
self.emit_computed_member_dynamic(assign, member, ctx)
}
} 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 name = member.field.name.as_str();
// 逻辑赋值:GET_PRIVATE 读旧值(brand 检查在读时执行)→ 短路测试 →
// 通过才求值 RHS 并 SET_PRIVATE 写回,结果统一为旧值或新值。
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 (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
let key_reg = self.emit_private_id_reg(name, ctx)?;
let result_reg = ctx.alloc_reg();
ctx.inst(Inst::get_private(
Operand::Reg(result_reg),
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
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::set_private(
Operand::Reg(obj_reg),
Operand::Reg(val_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
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 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)
if assign.operator != AssignmentOperator::Assign {
// 复合赋值保规范求值序:先 GET_PRIVATE 读旧值(brand 检查先于 RHS 副作用),
// 再求值 RHS,运算后 SET_PRIVATE 写回。
let val_reg = ctx.alloc_reg();
ctx.inst(Inst::get_private(
Operand::Reg(val_reg),
Operand::Reg(obj_reg),
Operand::Reg(key_reg),
brand_reg,
brand_id,
));
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)),
};
if assign.operator == AssignmentOperator::Exponential {
// 指数无独立二元指令,COMPOUND_EXP 语义 rd=rd^a:rhs 放 a 槽。
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(rhs), Operand::None));
} else {
ctx.inst(Inst::new(op, Operand::Reg(val_reg), Operand::Reg(val_reg), Operand::Reg(rhs)));
}
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 {
let val_reg = self.emit_expression(&assign.right, 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() {
+173
View File
@@ -637,3 +637,176 @@ fn class_generator_method_return_closes_with_value() {
.unwrap();
assert_eq!(vm.lookup_str(result).unwrap(), "1,99,true");
}
// ── 私有字段复合赋值(+= 等)与逻辑赋值(&&= ||= ??=)──
// data 字段复合赋值:旧值读取 → 运算 → 写回,表达式结果与后续读取一致。
#[test]
fn private_field_compound_assignment_writes_back() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; m(){ return this.#x += 2; } get(){ return this.#x; } } var c = new C(); c.m()*10 + c.get()",
)
.unwrap();
assert_num(result, 33.0);
}
// 复合赋值先读旧值再求值 RHS:RHS 改写同一字段时仍以旧值参与运算。
#[test]
fn private_field_compound_assignment_reads_old_value_before_rhs() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; m(){ return this.#x += (this.#x = 5); } get(){ return this.#x; } } var c = new C(); c.m()*10 + c.get()",
)
.unwrap();
// 旧值 1 先读,RHS 把 #x 写为 5,1+5=6 再写回 → 结果 6。
assert_num(result, 66.0);
}
// 减法/乘法/除法复合赋值序列,验证三操作数二元运算路径。
#[test]
fn private_field_compound_assignment_arithmetic_operators() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 10; a(){ return this.#x -= 3; } b(){ return this.#x *= 2; } c(){ return this.#x /= 4; } } var c = new C(); c.a()*10000 + c.b()*100 + c.c()",
)
.unwrap();
assert_num(result, 7.0 * 10000.0 + 14.0 * 100.0 + 3.5);
}
// 位与/左移/位或复合赋值序列。
#[test]
fn private_field_compound_assignment_bitwise_and_shift() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 0b1010; a(){ return this.#x &= 0b1100; } b(){ return this.#x <<= 1; } c(){ return this.#x |= 0b1; } } var c = new C(); c.a()*100 + c.b()*10 + c.c()",
)
.unwrap();
assert_num(result, 8.0 * 100.0 + 16.0 * 10.0 + 17.0);
}
// 指数复合赋值走 COMPOUND_EXP(rhs 在 a 槽)。
#[test]
fn private_field_exponential_compound_assignment() {
let mut vm = Vm::new();
let result = eval(&mut vm, "class C { #x = 2; m(){ return this.#x **= 3; } } new C().m()").unwrap();
assert_num(result, 8.0);
}
// 私有访问器复合赋值:getter 读旧值、setter 写新值。
#[test]
fn private_accessor_compound_assignment_uses_getter_setter() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { static get #x(){ return this._v; } static set #x(v){ this._v = v; } static m(){ this.#x = 1; return this.#x += 2; } static get(){ return this._v; } } C.m()*10 + C.get()",
)
.unwrap();
assert_num(result, 33.0);
}
// readonly-accessor 复合赋值:无 setter 抛 TypeError。
#[test]
fn private_readonly_accessor_compound_assignment_throws_type_error() {
let mut vm = Vm::new();
let err = eval(
&mut vm,
"class C { static get #x(){ return 1; } static m(){ return this.#x += 1; } } C.m()",
)
.unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
// 私有方法槽复合赋值:写方法槽抛 TypeError。
#[test]
fn private_method_compound_assignment_throws_type_error() {
let mut vm = Vm::new();
let err = eval(&mut vm, "class C { #m(){ return 1; } m(){ return this.#m += 1; } } new C().m()").unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
// 私有方法槽逻辑赋值(&&= 走写回路径)同样抛 TypeError。
#[test]
fn private_method_logical_assignment_throws_type_error() {
let mut vm = Vm::new();
let err = eval(&mut vm, "class C { #m(){ return 1; } m(){ return this.#m &&= 2; } } new C().m()").unwrap_err();
assert!(err.contains("TypeError"), "expected TypeError, got: {err}");
}
// ||= 对 truthy 旧值短路:RHS 副作用不执行,字段保持原值。
#[test]
fn private_field_logical_or_keeps_truthy_value_and_short_circuits() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; m(){ var n = 0; var r = this.#x ||= ++n; return r*10 + n; } } new C().m()",
)
.unwrap();
// #x=1 为 truthy → 短路,RHS 不执行,结果保持 1。
assert_num(result, 10.0);
}
// ||= 对 falsy 旧值写回 RHS 值。
#[test]
fn private_field_logical_or_assigns_when_falsy() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 0; m(){ return this.#x ||= 5; } get(){ return this.#x; } } var c = new C(); c.m()*10 + c.get()",
)
.unwrap();
assert_num(result, 55.0);
}
// &&= 对 truthy 旧值写回 RHS 值。
#[test]
fn private_field_logical_and_assigns_when_truthy() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; m(){ return this.#x &&= 2; } get(){ return this.#x; } } var c = new C(); c.m()*10 + c.get()",
)
.unwrap();
assert_num(result, 22.0);
}
// &&= 对 falsy 旧值短路:RHS 副作用不执行,字段保持原值。
#[test]
fn private_field_logical_and_short_circuits_on_falsy() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 0; m(){ var n = 0; var r = this.#x &&= ++n; return r*10 + n; } } new C().m()",
)
.unwrap();
// #x=0 为 falsy → 短路,RHS 不执行,结果保持 0。
assert_num(result, 0.0);
}
// ??= 对 nullish(未初始化字段为 undefined)写回 RHS 值。
#[test]
fn private_field_nullish_coalesce_assigns_when_nullish() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x; m(){ return this.#x ??= 7; } get(){ return this.#x; } } var c = new C(); c.m()*10 + c.get()",
)
.unwrap();
assert_num(result, 77.0);
}
// ??= 对已定义旧值短路:RHS 副作用不执行。
#[test]
fn private_field_nullish_coalesce_short_circuits_on_defined() {
let mut vm = Vm::new();
let result = eval(
&mut vm,
"class C { #x = 1; m(){ var n = 0; var r = this.#x ??= ++n; return r*10 + n; } } new C().m()",
)
.unwrap();
assert_num(result, 10.0);
}