mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: 对齐 Number 原型四方法规范序位并补齐绑定元数据面
This commit is contained in:
@@ -38,16 +38,23 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
proto,
|
||||
core,
|
||||
&[
|
||||
("toString", oxide_builtins::number::number_to_string::<crate::vm::Vm> as *const (), 0),
|
||||
("toFixed", oxide_builtins::number::number_to_fixed::<crate::vm::Vm> as *const (), 0),
|
||||
("toString", oxide_builtins::number::number_to_string::<crate::vm::Vm> as *const (), 1),
|
||||
("toFixed", oxide_builtins::number::number_to_fixed::<crate::vm::Vm> as *const (), 1),
|
||||
(
|
||||
"toExponential",
|
||||
oxide_builtins::number::number_to_exponential::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
1,
|
||||
),
|
||||
(
|
||||
"toPrecision",
|
||||
oxide_builtins::number::number_to_precision::<crate::vm::Vm> as *const (),
|
||||
1,
|
||||
),
|
||||
// toLocaleString 为 Number.prototype 的 own 属性(规范 21.7.3.25):
|
||||
// 保留参数不计 length(length 0),locale 参数忽略、恒十进制。
|
||||
(
|
||||
"toLocaleString",
|
||||
oxide_builtins::number::number_to_locale_string::<crate::vm::Vm> as *const (),
|
||||
0,
|
||||
),
|
||||
("valueOf", oxide_builtins::number::number_value_of::<crate::vm::Vm> as *const (), 0),
|
||||
@@ -85,4 +92,17 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
("isFinite", oxide_builtins::global::global_is_finite::<crate::vm::Vm> as *const (), 1),
|
||||
],
|
||||
);
|
||||
|
||||
// 规范(21.7.3 / 20.1.3 注):Number.parseInt/parseFloat 与全局同名函数
|
||||
// 是同一函数对象(SameValue)。ctor 侧绑定先执行、产生独立 wrapper,
|
||||
// 此处把 global 侧 wrapper 值原位写回 ctor 同名槽,描述符形态不变。
|
||||
for name in ["parseInt", "parseFloat"] {
|
||||
let si = core.perm_interner().intern(name).0;
|
||||
if let Some(global_pos) = core.shape_forge().lookup_position(global.shape_id(), si) {
|
||||
let val = global.get_prop_at(global_pos);
|
||||
if let Some(ctor_pos) = core.shape_forge().lookup_position(ctor.shape_id(), si) {
|
||||
ctor.set_prop_at(ctor_pos, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,8 @@ fn number_to_exponential() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var n = 123.456; n.toExponential(2)").unwrap();
|
||||
let s = vm.lookup_str(result).unwrap_or_default();
|
||||
assert_eq!(s, "1.23e2");
|
||||
// 指数恒带符号(规范科学式形态)。
|
||||
assert_eq!(s, "1.23e+2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -316,3 +317,133 @@ fn boxed_number_is_object_and_call_stays_primitive() {
|
||||
let n = eval(&mut vm, "Number('7')").unwrap();
|
||||
assert_eq!(n.as_int(), 7);
|
||||
}
|
||||
|
||||
fn str_of(vm: &mut Vm, source: &str) -> String {
|
||||
let r = eval(vm, source).unwrap();
|
||||
vm.lookup_str(r).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_proto_methods_this_number_value() {
|
||||
let mut vm = Vm::new();
|
||||
// Number 原始值与 Number 对象均合法 this。
|
||||
assert_eq!(str_of(&mut vm, "(new Number(7)).toFixed(1)"), "7.0");
|
||||
assert_eq!(str_of(&mut vm, "(new Number(7)).toPrecision(2)"), "7.0");
|
||||
assert_eq!(str_of(&mut vm, "(new Number(7)).toExponential(1)"), "7.0e+0");
|
||||
assert_eq!(str_of(&mut vm, "(new Number(255)).toString(16)"), "ff");
|
||||
// Number.prototype 本体是 [[NumberData]] = +0 的 Number 对象。
|
||||
assert_eq!(str_of(&mut vm, "Number.prototype.toFixed(1)"), "0.0");
|
||||
assert_eq!(str_of(&mut vm, "Number.prototype.toExponential(0)"), "0e+0");
|
||||
// 其余 this 一律 TypeError(四方法非通用)。
|
||||
for method in ["toString", "toFixed", "toExponential", "toPrecision"] {
|
||||
let src = format!("Number.prototype.{}.call({{}})", method);
|
||||
let err = eval(&mut vm, &src).unwrap_err();
|
||||
assert!(err.contains("TypeError"), "for {}: {}", method, err);
|
||||
}
|
||||
let err = eval(&mut vm, "Number.prototype.toFixed.call(new String('1'))").unwrap_err();
|
||||
assert!(err.contains("TypeError"), "got: {}", err);
|
||||
let err = eval(&mut vm, "Number.prototype.toPrecision.call(null)").unwrap_err();
|
||||
assert!(err.contains("TypeError"), "got: {}", err);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_methods_argument_coercion_and_ordering() {
|
||||
let mut vm = Vm::new();
|
||||
// 参数走完整 ToNumber:Symbol/BigInt 抛 TypeError。
|
||||
for method in ["toFixed", "toExponential", "toPrecision"] {
|
||||
let err = eval(&mut vm, &format!("(0).{}(0n)", method)).unwrap_err();
|
||||
assert!(err.contains("TypeError"), "bigint for {}: {}", method, err);
|
||||
let err = eval(&mut vm, &format!("(0).{}(Symbol())", method)).unwrap_err();
|
||||
assert!(err.contains("TypeError"), "symbol for {}: {}", method, err);
|
||||
}
|
||||
// 参数 valueOf 抛出的原始异常原样传播(保留异常值)。
|
||||
let v = eval(&mut vm, "try { (0).toFixed({valueOf(){ throw 42 }}) } catch (e) { e }").unwrap();
|
||||
assert_eq!(v.as_int(), 42);
|
||||
// toFixed:范围检查先于 NaN 短路。
|
||||
let err = eval(&mut vm, "NaN.toFixed(Infinity)").unwrap_err();
|
||||
assert!(err.contains("RangeError"), "got: {}", err);
|
||||
assert_eq!(str_of(&mut vm, "NaN.toFixed(0)"), "NaN");
|
||||
// toPrecision:NaN/±∞ 专名先于范围检查。
|
||||
assert_eq!(str_of(&mut vm, "NaN.toPrecision(1000)"), "NaN");
|
||||
assert_eq!(str_of(&mut vm, "(Infinity).toPrecision(1000)"), "Infinity");
|
||||
assert_eq!(str_of(&mut vm, "(-Infinity).toPrecision(1000)"), "-Infinity");
|
||||
let err = eval(&mut vm, "(10).toPrecision(0)").unwrap_err();
|
||||
assert!(err.contains("RangeError"), "got: {}", err);
|
||||
// toExponential:NaN/±∞ 专名先于范围检查。
|
||||
assert_eq!(str_of(&mut vm, "NaN.toExponential(101)"), "NaN");
|
||||
assert_eq!(str_of(&mut vm, "(Infinity).toExponential(101)"), "Infinity");
|
||||
let err = eval(&mut vm, "(3).toExponential(101)").unwrap_err();
|
||||
assert!(err.contains("RangeError"), "got: {}", err);
|
||||
let err = eval(&mut vm, "(3).toExponential(-1)").unwrap_err();
|
||||
assert!(err.contains("RangeError"), "got: {}", err);
|
||||
// toExponential 的 undefined 走最短式,非按 0 位处理。
|
||||
assert_eq!(str_of(&mut vm, "(123.456).toExponential(undefined)"), "1.23456e+2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_methods_default_precision_and_boundaries() {
|
||||
let mut vm = Vm::new();
|
||||
// toPrecision 缺省 precision = ToString(x)。
|
||||
assert_eq!(str_of(&mut vm, "(42).toPrecision()"), "42");
|
||||
assert_eq!(str_of(&mut vm, "(123.456).toPrecision(undefined)"), "123.456");
|
||||
assert_eq!(str_of(&mut vm, "NaN.toPrecision()"), "NaN");
|
||||
assert_eq!(str_of(&mut vm, "Number.prototype.toPrecision()"), "0");
|
||||
// toFixed 的 x ≥ 1e21 退化为 String 科学式。
|
||||
assert_eq!(str_of(&mut vm, "(1e21).toFixed()"), "1e+21");
|
||||
assert_eq!(str_of(&mut vm, "(-1e21).toFixed(0)"), "-1e+21");
|
||||
assert_eq!(str_of(&mut vm, "(new Number(1e21)).toFixed()"), "1e+21");
|
||||
// -0 三面:构造、toFixed、toExponential。
|
||||
assert_eq!(str_of(&mut vm, "String(Object.is(Number(-0), -0))"), "true");
|
||||
assert_eq!(str_of(&mut vm, "String(Object.is(new Number(-0).valueOf(), -0))"), "true");
|
||||
assert_eq!(str_of(&mut vm, "(-0).toFixed(0)"), "0");
|
||||
assert_eq!(str_of(&mut vm, "(-0).toExponential(0)"), "0e+0");
|
||||
assert_eq!(str_of(&mut vm, "(-0).toPrecision(2)"), "0.0");
|
||||
// 指数恒带符号。
|
||||
assert_eq!(str_of(&mut vm, "(3).toExponential(0)"), "3e+0");
|
||||
assert_eq!(str_of(&mut vm, "(0).toExponential(1)"), "0.0e+0");
|
||||
assert_eq!(str_of(&mut vm, "(10).toPrecision(1)"), "1e+1");
|
||||
assert_eq!(str_of(&mut vm, "(1.1e-32).toExponential()"), "1.1e-32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_isnan_isfinite_strict_type() {
|
||||
let mut vm = Vm::new();
|
||||
// 非 Number 类型(含字符串/对象)一律 false,不强转。
|
||||
assert_eq!(str_of(&mut vm, "String(Number.isFinite('1'))"), "false");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.isNaN('NaN'))"), "false");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.isFinite(new Number(42)))"), "false");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.isFinite(42))"), "true");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.isNaN(NaN))"), "true");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.isFinite(Infinity))"), "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_proto_tag_and_metadata() {
|
||||
let mut vm = Vm::new();
|
||||
// Number.prototype 本体 tag 为 Number(品牌表据 type_tag 命中)。
|
||||
assert_eq!(str_of(&mut vm, "Object.prototype.toString.call(Number.prototype)"), "[object Number]");
|
||||
// parseInt/parseFloat 与全局同名函数是同一函数对象。
|
||||
assert_eq!(str_of(&mut vm, "String(Number.parseInt === parseInt)"), "true");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.parseFloat === parseFloat)"), "true");
|
||||
// toLocaleString 为 own 属性,行为同 toString(locale 参数忽略)。
|
||||
assert_eq!(str_of(&mut vm, "String(Number.prototype.hasOwnProperty('toLocaleString'))"), "true");
|
||||
assert_eq!(str_of(&mut vm, "(1.5).toLocaleString()"), "1.5");
|
||||
// 四方法 length 为 1。
|
||||
assert_eq!(str_of(&mut vm, "String(Number.prototype.toString.length)"), "1");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.prototype.toFixed.length)"), "1");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.prototype.toExponential.length)"), "1");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.prototype.toPrecision.length)"), "1");
|
||||
}
|
||||
|
||||
/// full_reset 脏重建:number 家族重建后 Number.prototype 仍带 Number 对象 tag
|
||||
/// 与 +0 包值(漏此分支则 tag 翻回 "Object")。
|
||||
#[test]
|
||||
fn number_proto_tag_survives_dirty_rebuild() {
|
||||
let mut vm = Vm::new();
|
||||
// 属性写使 number 家族置脏,触发选择性重建路径。
|
||||
eval(&mut vm, "Number.prototype.marker = 1").unwrap();
|
||||
vm.full_reset();
|
||||
assert_eq!(str_of(&mut vm, "Object.prototype.toString.call(Number.prototype)"), "[object Number]");
|
||||
assert_eq!(str_of(&mut vm, "Number.prototype.toFixed(1)"), "0.0");
|
||||
assert_eq!(str_of(&mut vm, "String(Number.parseInt === parseInt)"), "true");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user