feat: 私有字段复合赋值 emit 支持(GET_PRIVATE→op→SET_PRIVATE 序列)
This commit is contained in:
@@ -214,22 +214,97 @@ impl Emitter {
|
|||||||
self.emit_computed_member_dynamic(assign, member, ctx)
|
self.emit_computed_member_dynamic(assign, member, ctx)
|
||||||
}
|
}
|
||||||
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
|
} else if let oxide_parser::AssignmentTarget::PrivateFieldExpression(member) = &assign.left {
|
||||||
if assign.operator != AssignmentOperator::Assign {
|
let name = member.field.name.as_str();
|
||||||
return Err("compound assignment to private fields not supported".into());
|
// 逻辑赋值: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 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 (brand_reg, brand_id) = self.private_access_brand(obj_reg, name, ctx)?;
|
||||||
let key_reg = self.emit_private_id_reg(name, ctx)?;
|
let key_reg = self.emit_private_id_reg(name, ctx)?;
|
||||||
ctx.inst(Inst::set_private(
|
if assign.operator != AssignmentOperator::Assign {
|
||||||
Operand::Reg(obj_reg),
|
// 复合赋值保规范求值序:先 GET_PRIVATE 读旧值(brand 检查先于 RHS 副作用),
|
||||||
Operand::Reg(val_reg),
|
// 再求值 RHS,运算后 SET_PRIVATE 写回。
|
||||||
Operand::Reg(key_reg),
|
let val_reg = ctx.alloc_reg();
|
||||||
brand_reg,
|
ctx.inst(Inst::get_private(
|
||||||
brand_id,
|
Operand::Reg(val_reg),
|
||||||
));
|
Operand::Reg(obj_reg),
|
||||||
Ok(val_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 {
|
} else if let oxide_parser::AssignmentTarget::AssignmentTargetIdentifier(id_ref) = &assign.left {
|
||||||
if assign.operator != AssignmentOperator::Assign {
|
if assign.operator != AssignmentOperator::Assign {
|
||||||
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
if let Some(logical_op) = assign.operator.to_logical_operator() {
|
||||||
|
|||||||
@@ -637,3 +637,176 @@ fn class_generator_method_return_closes_with_value() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(vm.lookup_str(result).unwrap(), "1,99,true");
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user