mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 02:31:13 +08:00
575 lines
23 KiB
Rust
575 lines
23 KiB
Rust
use std::sync::Arc;
|
||
|
||
use oxide_compiler::compiler::Compiler;
|
||
use oxide_types::value::JsValue;
|
||
use oxide_vm::vm::Vm;
|
||
|
||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||
let allocator = oxide_parser::Allocator::default();
|
||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||
vm.run(&Arc::new(module))
|
||
}
|
||
|
||
#[test]
|
||
fn number_is_nan_with_nan() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number.isNaN(NaN)").unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn number_is_nan_with_number() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number.isNaN(42)").unwrap();
|
||
assert!(!result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn number_is_finite_with_number() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number.isFinite(42)").unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn number_is_finite_with_infinity() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number.isFinite(1/0)").unwrap();
|
||
assert!(!result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn parse_int_decimal() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "parseInt('42')").unwrap();
|
||
assert_eq!(result.as_int(), 42);
|
||
}
|
||
|
||
#[test]
|
||
fn parse_int_hex() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "parseInt('0xFF')").unwrap();
|
||
assert_eq!(result.as_int(), 255);
|
||
}
|
||
|
||
#[test]
|
||
fn parse_float_decimal() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "parseFloat('3.14')").unwrap();
|
||
#[allow(clippy::approx_constant)]
|
||
let expected = 3.14;
|
||
assert!((result.as_double() - expected).abs() < 0.001);
|
||
}
|
||
|
||
#[test]
|
||
fn parse_float_invalid() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "parseFloat('abc')").unwrap();
|
||
assert!(result.as_double().is_nan());
|
||
}
|
||
|
||
#[test]
|
||
fn parse_int_prefix_and_radix_semantics() {
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
// 前缀解析:遇非法字符停止,取最长合法数字前缀。
|
||
("parseInt('123abc') === 123", true),
|
||
// +/- 符号前缀。
|
||
("parseInt('+42') === 42", true),
|
||
("parseInt('-42') === -42", true),
|
||
// radix 显式时不触发 0x;缺省/0/16 时触发。
|
||
("parseInt('0x10', 10) === 0", true),
|
||
("parseInt('0x10') === 16", true),
|
||
("parseInt('0x10', 0) === 16", true),
|
||
("parseInt('0x10', 16) === 16", true),
|
||
("parseInt('+0x10') === 16", true),
|
||
("parseInt('0XFF') === 255", true),
|
||
// 超 i32 用 f64 近似(允许舍入),i32 域内保持精确。
|
||
("parseInt('99999999999999999999') === 1e20", true),
|
||
("parseInt('9007199254740993') === 9007199254740992", true),
|
||
("parseInt('2147483647') === 2147483647", true),
|
||
("parseInt('2147483648') === 2147483648", true),
|
||
// radix 有效字符过滤与空白处理。
|
||
("parseInt('10', 2) === 2", true),
|
||
("parseInt('1a', 2) === 1", true),
|
||
("parseInt(' -42 ') === -42", true),
|
||
// 负零保留符号。
|
||
("1 / parseInt('-0') === -Infinity", true),
|
||
// 无有效数字 / radix 越界 / 超大 radix 经 ToInt32 回绕。
|
||
("isNaN(parseInt(''))", true),
|
||
("isNaN(parseInt('0x'))", true),
|
||
("isNaN(parseInt('0xG'))", true),
|
||
("isNaN(parseInt('z'))", true),
|
||
("isNaN(parseInt('10', 1))", true),
|
||
("isNaN(parseInt('10', 37))", true),
|
||
("isNaN(parseInt('z', 2 ** 40))", true),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn parse_float_prefix_semantics() {
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
// 前缀解析:取最长合法 StrDecimalLiteral。
|
||
("parseFloat('3.14abc') === 3.14", true),
|
||
("parseFloat('1.2.3') === 1.2", true),
|
||
("parseFloat('1e5') === 100000", true),
|
||
("parseFloat('1e+5') === 100000", true),
|
||
("parseFloat('.5') === 0.5", true),
|
||
("parseFloat(' 3.14 ') === 3.14", true),
|
||
// Infinity 大小写敏感,可带符号与后缀。
|
||
("parseFloat('Infinity') === Infinity", true),
|
||
("parseFloat('+Infinity') === Infinity", true),
|
||
("parseFloat('-Infinity') === -Infinity", true),
|
||
("parseFloat('Infinityx') === Infinity", true),
|
||
("isNaN(parseFloat('-infinity'))", true),
|
||
// 十六进制不解析,十进制前缀在 'x' 处停止;溢出归 Infinity。
|
||
("parseFloat('0x10') === 0", true),
|
||
("parseFloat('1e400') === Infinity", true),
|
||
("parseFloat('-1e400') === -Infinity", true),
|
||
// 无合法前缀。
|
||
("isNaN(parseFloat(''))", true),
|
||
("isNaN(parseFloat('.'))", true),
|
||
("isNaN(parseFloat('.e5'))", true),
|
||
("isNaN(parseFloat('abc'))", true),
|
||
// 指数后无数字:'e' 不构成 ExponentPart,最长合法前缀为整数部分。
|
||
("parseFloat('1e') === 1", true),
|
||
("parseFloat('1e+') === 1", true),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
assert_eq!(result.as_bool(), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn parse_parse_int_object_arg_tostring() {
|
||
let mut vm = Vm::new();
|
||
// 对象参数经 ToString 完整转换:取 toString 结果再解析。
|
||
let result = eval(&mut vm, "parseInt({toString: () => '42'})").unwrap();
|
||
assert_eq!(result.as_int(), 42);
|
||
#[allow(clippy::approx_constant)]
|
||
let expected = 3.14;
|
||
let result = eval(&mut vm, "parseFloat({toString: () => '3.14abc'})").unwrap();
|
||
assert!((result.as_double() - expected).abs() < 0.001);
|
||
// Symbol 参数按规范抛 TypeError。
|
||
let err = eval(&mut vm, "parseInt(Symbol())").unwrap_err();
|
||
assert!(err.contains("TypeError"), "got: {}", err);
|
||
let err = eval(&mut vm, "parseFloat(Symbol('x'))").unwrap_err();
|
||
assert!(err.contains("TypeError"), "got: {}", err);
|
||
// 对象 toString 抛出的原始异常原样传播(保留异常对象)。
|
||
let err = eval(&mut vm, "try { parseInt({toString(){ throw 42 }}) } catch (e) { e }").unwrap();
|
||
assert_eq!(err.as_int(), 42);
|
||
}
|
||
|
||
#[test]
|
||
fn number_constructor() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number('42')").unwrap();
|
||
assert_eq!(result.as_int(), 42);
|
||
}
|
||
|
||
#[test]
|
||
fn to_string_of_number() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "var n = 42; n.toString()").unwrap();
|
||
let s = vm.lookup_str(result).unwrap_or_default();
|
||
assert_eq!(s, "42");
|
||
}
|
||
|
||
#[test]
|
||
fn to_fixed_of_number() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "var n = 3.14159; n.toFixed(2)").unwrap();
|
||
let s = vm.lookup_str(result).unwrap_or_default();
|
||
assert_eq!(s, "3.14");
|
||
}
|
||
|
||
#[test]
|
||
fn to_string_scientific_boundaries() {
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
("(1e21).toString()", "1e+21"),
|
||
("(1e20).toString()", "100000000000000000000"),
|
||
("(123.45).toString()", "123.45"),
|
||
("(0.000001).toString()", "0.000001"),
|
||
("(0.0000001).toString()", "1e-7"),
|
||
("(1e15).toString()", "1000000000000000"),
|
||
("(0.1+0.2).toString()", "0.30000000000000004"),
|
||
("String(1e21)", "1e+21"),
|
||
("(\"\" + 1e21)", "1e+21"),
|
||
("(-1e21).toString()", "-1e+21"),
|
||
("JSON.stringify(1e21)", "1e+21"),
|
||
("JSON.stringify(1e20)", "100000000000000000000"),
|
||
// 整数 double 快路径(2^53 内直写)。
|
||
("(42.0).toString()", "42"),
|
||
("(1e10).toString()", "10000000000"),
|
||
("String(-42.0)", "-42"),
|
||
("(-0).toString()", "0"),
|
||
("JSON.stringify(1.5)", "1.5"),
|
||
("JSON.stringify(-0)", "0"),
|
||
// 2^53 边界与 1e22:超出快路径范围仍输出规范形态。
|
||
("Math.pow(2,53).toString()", "9007199254740992"),
|
||
("Math.pow(2,63).toString()", "9223372036854776000"),
|
||
("(1e22).toString()", "1e+22"),
|
||
("(1.5e20).toString()", "150000000000000000000"),
|
||
("(9999999999999999).toString()", "10000000000000000"),
|
||
];
|
||
for (src, expected) in cases {
|
||
let result = eval(&mut vm, src).unwrap();
|
||
let s = vm.lookup_str(result).unwrap_or_default();
|
||
assert_eq!(s, expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn to_string_radix_and_range_error() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "(255).toString(16)").unwrap();
|
||
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "ff");
|
||
|
||
let result = eval(&mut vm, "(2).toString(2)").unwrap();
|
||
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "10");
|
||
|
||
let err = eval(&mut vm, "(10).toString(1)").unwrap_err();
|
||
assert!(err.contains("RangeError"), "got: {}", err);
|
||
|
||
let err = eval(&mut vm, "(10).toString(37)").unwrap_err();
|
||
assert!(err.contains("RangeError"), "got: {}", err);
|
||
}
|
||
|
||
#[test]
|
||
fn number_constructor_string_rules() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number('0xa')").unwrap();
|
||
assert_eq!(result.as_int(), 10);
|
||
|
||
let result = eval(&mut vm, "Number('')").unwrap();
|
||
assert_eq!(result.as_int(), 0);
|
||
|
||
let result = eval(&mut vm, "Number('INFINITY')").unwrap();
|
||
assert!(result.as_double().is_nan());
|
||
}
|
||
|
||
#[test]
|
||
fn number_is_integer_true() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number.isInteger(42)").unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn number_is_integer_false() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number.isInteger(42.5)").unwrap();
|
||
assert!(!result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn number_epsilon_is_positive() {
|
||
let mut vm = Vm::new();
|
||
let result = eval(&mut vm, "Number.EPSILON > 0").unwrap();
|
||
assert!(result.as_bool());
|
||
}
|
||
|
||
#[test]
|
||
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.23e+2");
|
||
}
|
||
|
||
#[test]
|
||
fn number_static_constants_remain_bound() {
|
||
let mut vm = Vm::new();
|
||
assert_eq!(eval(&mut vm, "Number.MAX_SAFE_INTEGER").unwrap().as_double(), 9007199254740991f64);
|
||
assert!(eval(&mut vm, "Number.POSITIVE_INFINITY > 0").unwrap().as_bool());
|
||
}
|
||
|
||
#[test]
|
||
fn boxed_number_valueof_preserves_int_and_float() {
|
||
let mut vm = Vm::new();
|
||
let i = eval(&mut vm, "new Number(5).valueOf()").unwrap();
|
||
assert!(i.is_int());
|
||
assert_eq!(i.as_int(), 5);
|
||
|
||
let f = eval(&mut vm, "new Number(1.5).valueOf()").unwrap();
|
||
assert!(f.is_double());
|
||
assert_eq!(f.as_double(), 1.5);
|
||
|
||
let s = eval(&mut vm, "new Number('7').valueOf()").unwrap();
|
||
assert_eq!(s.as_int(), 7);
|
||
}
|
||
|
||
#[test]
|
||
fn boxed_number_is_object_and_call_stays_primitive() {
|
||
let mut vm = Vm::new();
|
||
let ty = eval(&mut vm, "typeof new Number(5)").unwrap();
|
||
assert_eq!(vm.lookup_str(ty).unwrap(), "object");
|
||
|
||
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");
|
||
}
|
||
|
||
// 全表值对照 node v20.19.2 逐位核定(half-up 对 half-even 的分歧点、
|
||
// 进位传播、位数增长、精确展开面、零与 -0 三面)。
|
||
#[test]
|
||
fn number_format_tie_half_up_and_carry() {
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
// tie 取较大者(away-from-zero),负数同形。
|
||
("(2.5).toExponential(0)", "3e+0"),
|
||
("(2.4).toExponential(0)", "2e+0"),
|
||
("(-2.5).toExponential(0)", "-3e+0"),
|
||
("(-0.5).toExponential(0)", "-5e-1"),
|
||
("(0.5).toFixed(0)", "1"),
|
||
("(0.4).toFixed(0)", "0"),
|
||
("(2.5).toFixed(0)", "3"),
|
||
("(-0.5).toFixed(0)", "-1"),
|
||
("(2.675).toFixed(2)", "2.67"),
|
||
// 精确展开低于 tie 点时不得进位(仅看最短形会误进位)。
|
||
("(1.15).toFixed(1)", "1.1"),
|
||
("(1.005).toFixed(2)", "1.00"),
|
||
("(1.005).toFixed(1)", "1.0"),
|
||
("(0.05).toFixed(1)", "0.1"),
|
||
("(0.145).toFixed(2)", "0.14"),
|
||
("(1.2345).toFixed(3)", "1.234"),
|
||
("(1.0005).toFixed(3)", "1.000"),
|
||
("(0.615).toFixed(2)", "0.61"),
|
||
// 进位传播与位数增长(9.99→10.0 型)。
|
||
("(9.999).toFixed(1)", "10.0"),
|
||
("(9.999).toFixed(0)", "10"),
|
||
("(-9.999).toFixed(1)", "-10.0"),
|
||
("(0.9999).toExponential(0)", "1e+0"),
|
||
("(-0.9999).toExponential(0)", "-1e+0"),
|
||
("(9.9999999999999999).toFixed(0)", "10"),
|
||
("(999).toPrecision(2)", "1.0e+3"),
|
||
("(-999).toPrecision(2)", "-1.0e+3"),
|
||
("(99.99).toPrecision(3)", "100"),
|
||
("(999999).toPrecision(2)", "1.0e+6"),
|
||
("(25).toExponential(0)", "3e+1"),
|
||
("(12345).toExponential(3)", "1.235e+4"),
|
||
// 精确整数展开(位数超最短形)。
|
||
("(1000000000000000128).toFixed(0)", "1000000000000000128"),
|
||
("(9007199254740993).toFixed(0)", "9007199254740992"),
|
||
("(9.9e20).toFixed(0)", "990000000000000000000"),
|
||
// 进位后 e 决定定点/指数分界。
|
||
("(1.23456).toPrecision(4)", "1.235"),
|
||
("(123456).toPrecision(3)", "1.23e+5"),
|
||
("(1e-6).toPrecision(2)", "0.0000010"),
|
||
("(1e-7).toPrecision(2)", "1.0e-7"),
|
||
("(0.5).toPrecision(1)", "0.5"),
|
||
("(0.05).toPrecision(2)", "0.050"),
|
||
("(10).toPrecision(1)", "1e+1"),
|
||
// 零与 -0 三面(-0 落正分支)。
|
||
("(0).toFixed(3)", "0.000"),
|
||
("(0).toExponential(2)", "0.00e+0"),
|
||
("(0).toPrecision(2)", "0.0"),
|
||
("(-0).toFixed(3)", "0.000"),
|
||
("(-0).toExponential(2)", "0.00e+0"),
|
||
("(-0).toPrecision(2)", "0.0"),
|
||
// x ≥ 1e21 走科学式 String(f64 已达 1e21)。
|
||
("(1e21-1).toFixed(0)", "1e+21"),
|
||
("(-1e21).toFixed(0)", "-1e+21"),
|
||
("(1234567890123456789012345678901234567890).toFixed(0)", "1.2345678901234568e+39"),
|
||
// 长小数面(展开终止后按 0 补齐)。
|
||
("(3.141592653589793).toPrecision(17)", "3.1415926535897931"),
|
||
(
|
||
"(3.141592653589793).toFixed(100)",
|
||
"3.1415926535897931159979634685441851615905761718750000000000000000000000000000000000000000000000000000",
|
||
),
|
||
// 次正规数与超大指数面。
|
||
("(1e308).toPrecision(2)", "1.0e+308"),
|
||
("(1.5e308).toExponential(1)", "1.5e+308"),
|
||
("(1e-323).toExponential(2)", "9.88e-324"),
|
||
("(5e-324).toExponential(0)", "5e-324"),
|
||
("(4.9e-324).toFixed(1)", "0.0"),
|
||
];
|
||
for (src, expected) in cases {
|
||
assert_eq!(str_of(&mut vm, src), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn number_format_default_shortest_digits() {
|
||
let mut vm = Vm::new();
|
||
// 缺省位数 = 最短精确有效数字数 − 1(round-trip 前缀,非任意最短十进制形)。
|
||
let cases = [
|
||
("(123.456).toExponential()", "1.23456e+2"),
|
||
("(2).toExponential()", "2e+0"),
|
||
("(0.1).toExponential()", "1e-1"),
|
||
("(0.3).toExponential()", "3e-1"),
|
||
("(1e-7).toExponential()", "1e-7"),
|
||
("(1.1e-32).toExponential()", "1.1e-32"),
|
||
("(1e20).toExponential()", "1e+20"),
|
||
("(1e-20).toExponential()", "1e-20"),
|
||
("(0.1+0.2).toExponential()", "3.0000000000000004e-1"),
|
||
("(123456789012345678901234567890.5).toExponential()", "1.2345678901234568e+29"),
|
||
("(42).toPrecision()", "42"),
|
||
("(0.0000001).toPrecision(3)", "1.00e-7"),
|
||
("(0.00000012345).toPrecision(2)", "1.2e-7"),
|
||
];
|
||
for (src, expected) in cases {
|
||
assert_eq!(str_of(&mut vm, src), expected, "for {}", src);
|
||
}
|
||
}
|
||
|
||
// 二进指数 q 为 limb 位宽整数倍(q ≡ 0 mod 32,偏置指数 e2 ≡ 19 mod 32)
|
||
// 的分数面:余数掩码边界恰落整 limb,digit 提取失真即此面(对照 node
|
||
// v20.19.2 逐位核定)。
|
||
#[test]
|
||
fn number_format_limb_aligned_fraction_digits() {
|
||
let mut vm = Vm::new();
|
||
let cases = [
|
||
// q = 64([2^-12, 2^-11) 量级)。
|
||
("(2 ** -12).toExponential(5)", "2.44141e-4"),
|
||
("(2 ** -12).toExponential(15)", "2.441406250000000e-4"),
|
||
// q = 32([2^20, 2^21) 量级)。
|
||
("(1048576.0000000002).toExponential(15)", "1.048576000000000e+6"),
|
||
("(1048576.0000000002).toFixed(10)", "1048576.0000000002"),
|
||
// 非受影响类的整数面守卫(小数值与 2^32 + 1)。
|
||
("(4.294967297).toFixed(2)", "4.29"),
|
||
("(4294967297).toFixed(2)", "4294967297.00"),
|
||
];
|
||
for (src, expected) in cases {
|
||
assert_eq!(str_of(&mut vm, src), expected, "for {}", src);
|
||
}
|
||
}
|