From 78976cad0c5f6adebc7f8f6dac4b674f795ab7d2 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 15 Aug 2026 21:17:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BA=8C=E5=85=83=E5=B9=82=E8=BF=90?= =?UTF-8?q?=E7=AE=97=20opcode=20=E6=94=AF=E6=8C=81=20number=20=E4=B8=8E=20?= =?UTF-8?q?BigInt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 EXP opcode(0x94)与 emit 层 Exponential 分支,二元 a ** b 不再 compile error - dispatch_exp 与 dispatch_compound_exp 共享 exp_result:IEEE-754 powf 快路径 + BigInt 幂(负/超 u32 指数 RangeError)+ 混合 BigInt/Number TypeError - 复合 **= 补 BigInt 语义(此前走 f64 近似),与二元 ** 行为一致 --- crates/oxide_bytecode/src/opcode.rs | 4 ++ crates/oxide_emit/src/expr/operator/mod.rs | 2 +- crates/oxide_vm/src/dispatch/arithmetic.rs | 73 +++++++++++++++++++--- crates/oxide_vm/src/vm.rs | 4 ++ crates/oxide_vm/tests/arithmetic_tests.rs | 50 +++++++++++++++ 5 files changed, 125 insertions(+), 8 deletions(-) diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index 7b43b15f..c3a932f2 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -193,6 +193,10 @@ define_opcodes! { COMPOUND_EXP = 0x0B => "COMPOUND_EXP", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A)], pure = false, jump = false, term = false, ic = false, + // 二元幂运算 `a ** b`(三寄存器,与 SUB/MUL 同族;0x00-0x0F 已满,值用空闲槽 0x94)。 + EXP = 0x94 => "EXP", + def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], + pure = true, jump = false, term = false, ic = false, MOV = 0x0C => "MOV", // rd=dst, a=src(寄存器复制,区间拆分搬值) def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A)], pure = true, jump = false, term = false, ic = false, diff --git a/crates/oxide_emit/src/expr/operator/mod.rs b/crates/oxide_emit/src/expr/operator/mod.rs index 36c38316..0f3324e9 100644 --- a/crates/oxide_emit/src/expr/operator/mod.rs +++ b/crates/oxide_emit/src/expr/operator/mod.rs @@ -57,6 +57,7 @@ impl Emitter { 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, @@ -73,7 +74,6 @@ impl Emitter { BinaryOperator::Instanceof => OpCode::INSTANCEOF, BinaryOperator::StrictEquality => OpCode::STRICT_EQ, BinaryOperator::StrictInequality => OpCode::STRICT_NEQ, - _ => return Err(format!("unsupported binary operator: {:?}", bin.operator)), }; ctx.inst(Inst::new(op, Operand::Reg(left), Operand::Reg(left), Operand::Reg(right))); Ok(left) diff --git a/crates/oxide_vm/src/dispatch/arithmetic.rs b/crates/oxide_vm/src/dispatch/arithmetic.rs index 04425786..1992dc4d 100644 --- a/crates/oxide_vm/src/dispatch/arithmetic.rs +++ b/crates/oxide_vm/src/dispatch/arithmetic.rs @@ -462,18 +462,77 @@ impl Vm { Ok(()) } + /// 幂运算核心:`base ** exponent` 的 Number/BigInt 双语义,二元 `**` 与复合 + /// `**=` 共用(避免两套实现漂移)。int/double 快路径与混合转数值路径都经 + /// IEEE-754 `powf`(`(-2) ** 0.5` → NaN、`0 ** 0` → 1、负底数整数指数等 + /// 边界由 libm pow 保证与规范一致)。 + /// + /// # 边界与前提 + /// - BigInt 指数为负或超 u32:抛 RangeError(BigInt::exponentiate 禁止负指数, + /// 超 u32 指数会分配不可控内存,同样拒绝)。 + /// - 混合 BigInt/Number → TypeError(ToNumeric 类型不一致)。 + /// - 对象操作数先 ToPrimitive(coerce 后判定,包装对象如 Object(2n) 参与 BigInt 运算)。 + /// + /// # 副作用 + /// - 可能新建 BigInt 会话值(new_bigint)。 + fn exp_result(&mut self, lv: JsValue, rv: JsValue) -> Result { + if lv.is_int() && rv.is_int() { + return Ok(JsValue::float((lv.as_int() as f64).powf(rv.as_int() as f64))); + } + if lv.is_double() && rv.is_double() { + return Ok(JsValue::float(lv.as_double().powf(rv.as_double()))); + } + if lv.is_bigint() && rv.is_bigint() { + let base = self.bigint_value(lv).clone(); + let exp = self.bigint_value(rv).clone(); + return self.bigint_exp(&base, &exp); + } + let l = self.coerce_primitive_bounded(lv, false)?; + let r = self.coerce_primitive_bounded(rv, false)?; + if l.is_bigint() && r.is_bigint() { + let base = self.bigint_value(l).clone(); + let exp = self.bigint_value(r).clone(); + return self.bigint_exp(&base, &exp); + } + if l.is_bigint() != r.is_bigint() { + // 包装对象 coerce 后暴露 BigInt:与另一非 BigInt 操作数混合必须抛 + // TypeError(同 ADD/SUB 抛点)。 + self.raise_type_error("Cannot mix BigInt and other types, use explicit conversions")?; + return Ok(JsValue::undefined()); + } + let ln = coercion::to_number(l); + let rn = coercion::to_number(r); + Ok(JsValue::float(ln.powf(rn))) + } + + /// BigInt 幂:负指数或超 u32 抛 RangeError,否则 `base.pow(exp)`。 + fn bigint_exp(&mut self, base: &num_bigint::BigInt, exp: &num_bigint::BigInt) -> Result { + let exp_u32 = match u32::try_from(exp) { + Ok(e) => e, + Err(_) => { + self.raise_error_kind("RangeError", "Exponent must be positive")?; + return Ok(JsValue::undefined()); + } + }; + Ok(self.new_bigint(base.pow(exp_u32))) + } + + /// 二元幂运算:`regs[rd] = regs[a] ** regs[b]`。 + #[inline(always)] + pub(crate) fn dispatch_exp(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { + vm_trace!("EXP rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]); + let lv = self.regs[a]; + let rv = self.regs[b]; + self.regs[rd] = self.exp_result(lv, rv)?; + Ok(()) + } + #[inline(always)] pub(crate) fn dispatch_compound_exp(&mut self, rd: usize, a: usize) -> Result<(), String> { vm_trace!("COMPOUND_EXP rd={} r{}={:?}", rd, a, self.regs[a]); let lv = self.regs[rd]; let rv = self.regs[a]; - if lv.is_int() && rv.is_int() { - self.regs[rd] = JsValue::float((lv.as_int() as f64).powf(rv.as_int() as f64)); - return Ok(()); - } - let l = self.coerce_number_bounded(lv)?; - let r = self.coerce_number_bounded(rv)?; - self.regs[rd] = JsValue::float(l.powf(r)); + self.regs[rd] = self.exp_result(lv, rv)?; Ok(()) } diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 2a6d7b27..7158282f 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -1470,6 +1470,10 @@ impl Vm { binary_arith!(self, a, b, rd, %, true); } + OpCode::EXP => { + self.dispatch_exp(rd, a, b)?; + } + OpCode::NEG => { self.dispatch_neg(rd, a)?; } diff --git a/crates/oxide_vm/tests/arithmetic_tests.rs b/crates/oxide_vm/tests/arithmetic_tests.rs index 5db26156..748df734 100644 --- a/crates/oxide_vm/tests/arithmetic_tests.rs +++ b/crates/oxide_vm/tests/arithmetic_tests.rs @@ -120,6 +120,56 @@ fn eval_compound_exp() { assert_eq!(eval("var x=2; x**=3; x"), "8"); } +#[test] +fn eval_binary_exp() { + assert_eq!(eval("2 ** 3"), "8"); + assert_eq!(eval("2 ** 10"), "1024"); + assert_eq!(eval("2 ** -1"), "0.5"); + assert_eq!(eval("4 ** 0.5"), "2"); +} + +#[test] +fn eval_exp_edge_cases() { + assert_eq!(eval("0 ** 0"), "1"); + assert_eq!(eval("(-2) ** 3"), "-8"); + assert_eq!(eval("(-2) ** 2"), "4"); + assert_eq!(eval("(-2) ** 0.5"), "NaN"); + assert_eq!(eval("NaN ** 0"), "1"); + assert_eq!(eval("2 ** Infinity"), "Infinity"); + assert_eq!(eval("2.5 ** 2"), "6.25"); +} + +#[test] +fn eval_exp_bigint() { + assert_eq!(eval("2n ** 3n"), "BigInt(8)"); + assert_eq!(eval("2n ** 0n"), "BigInt(1)"); + assert_eq!(eval("0n ** 0n"), "BigInt(1)"); +} + +#[test] +fn eval_exp_bigint_negative_exponent_throws() { + // BigInt::exponentiate 禁止负指数,抛 RangeError(规范 §6.1.6.1.19)。 + let err = eval("2n ** -1n"); + assert!(err.contains("RangeError"), "expected RangeError, got: {err}"); +} + +#[test] +fn eval_exp_mixed_bigint_number_throws() { + // ToNumeric 类型不一致:Number 与 BigInt 混合幂抛 TypeError(与 ADD/SUB 同)。 + let err = eval("2 ** 2n"); + assert!(err.contains("TypeError"), "expected TypeError, got: {err}"); + let err2 = eval("2n ** 2"); + assert!(err2.contains("TypeError"), "expected TypeError, got: {err2}"); +} + +#[test] +fn eval_exp_compound_bigint() { + // 复合 **= 与二元 ** 共享幂语义(BigInt 结果保持 BigInt,不再降 f64)。 + assert_eq!(eval("var x = 2n; x **= 3n; x"), "BigInt(8)"); + let err = eval("var x = 2n; x **= 3; x"); + assert!(err.contains("TypeError"), "expected TypeError, got: {err}"); +} + #[test] fn eval_compound_add_expr_value() { assert_eq!(eval("var x=5; x+=3"), "8");