mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: Date setter 族强转序与 TimeClip 规范对齐
This commit is contained in:
+1
-1
@@ -25,7 +25,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.19.14"
|
||||
version = "0.19.15"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -175,6 +175,16 @@ fn local_components(ms: f64) -> Option<(i64, i64, i64, i64, i64, i64, i64)> {
|
||||
))
|
||||
}
|
||||
|
||||
/// MakeFullYear(构造器与 Date.UTC 专用,setter 无此映射):先按 ToIntegerOrInfinity
|
||||
/// 截断,0 至 99 区间加 1900;NaN 与 ±Infinity 归 NaN。
|
||||
fn make_full_year(y: f64) -> Option<f64> {
|
||||
if !y.is_finite() {
|
||||
return None;
|
||||
}
|
||||
let yt = y.trunc();
|
||||
Some(if (0.0..=99.0).contains(&yt) { yt + 1900.0 } else { yt })
|
||||
}
|
||||
|
||||
/// TimeClip:|ts| > 8.64e15 或非有限的时刻值归 NaN(Invalid Date)。
|
||||
fn time_clip(ts: f64) -> f64 {
|
||||
if ts.is_finite() && (-MS_LIMIT as f64..=MS_LIMIT as f64).contains(&ts) {
|
||||
@@ -373,19 +383,16 @@ pub fn date_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let timestamp = if args.len() < 2 {
|
||||
Utc::now().timestamp_millis() as f64
|
||||
} else if args.len() > 2 {
|
||||
let y_val = oxide_runtime_api::to_number(vm.reg(args[1]));
|
||||
// 年 0..99 按规范映射到 1900..1999(ToInteger 截断后判断)。
|
||||
let y_val = if (0.0..=99.0).contains(&y_val.trunc()) {
|
||||
y_val.trunc() + 1900.0
|
||||
} else {
|
||||
y_val
|
||||
};
|
||||
let m_val = oxide_runtime_api::to_number(vm.reg(args[2]));
|
||||
let d_val = if args.len() > 3 { oxide_runtime_api::to_number(vm.reg(args[3])) } else { 1.0 };
|
||||
let h_val = if args.len() > 4 { oxide_runtime_api::to_number(vm.reg(args[4])) } else { 0.0 };
|
||||
let min_val = if args.len() > 5 { oxide_runtime_api::to_number(vm.reg(args[5])) } else { 0.0 };
|
||||
let sec_val = if args.len() > 6 { oxide_runtime_api::to_number(vm.reg(args[6])) } else { 0.0 };
|
||||
let ms_val = if args.len() > 7 { oxide_runtime_api::to_number(vm.reg(args[7])) } else { 0.0 };
|
||||
// 七参按序经完整 ToNumber 强转(对象参走 valueOf/toString,异常原样传播)。
|
||||
let y_val = native_try!(date_arg_number(vm, vm.reg(args[1])));
|
||||
let m_val = native_try!(date_arg_number(vm, vm.reg(args[2])));
|
||||
let d_val = if args.len() > 3 { native_try!(date_arg_number(vm, vm.reg(args[3]))) } else { 1.0 };
|
||||
let h_val = if args.len() > 4 { native_try!(date_arg_number(vm, vm.reg(args[4]))) } else { 0.0 };
|
||||
let min_val = if args.len() > 5 { native_try!(date_arg_number(vm, vm.reg(args[5]))) } else { 0.0 };
|
||||
let sec_val = if args.len() > 6 { native_try!(date_arg_number(vm, vm.reg(args[6]))) } else { 0.0 };
|
||||
let ms_val = if args.len() > 7 { native_try!(date_arg_number(vm, vm.reg(args[7]))) } else { 0.0 };
|
||||
// MakeFullYear:先截断后做 0..99 映射(±Infinity 归 NaN)。
|
||||
let y_val = make_full_year(y_val).unwrap_or(f64::NAN);
|
||||
if y_val.is_nan()
|
||||
|| m_val.is_nan()
|
||||
|| d_val.is_nan()
|
||||
@@ -516,8 +523,11 @@ pub fn date_utc<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if y.is_nan() || m.is_nan() || d.is_nan() || h.is_nan() || min.is_nan() || sec.is_nan() || ms.is_nan() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
// 年 0..99 按规范映射到 1900..1999(ToInteger 截断后判断)。
|
||||
let y = if (0.0..=99.0).contains(&y.trunc()) { y + 1900.0 } else { y };
|
||||
// MakeFullYear:先截断后做 0..99 映射(±Infinity 归 NaN)。
|
||||
let y = match make_full_year(y) {
|
||||
Some(y) => y,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
match make_day(y, m, d).and_then(|days| make_time(h, min, sec, ms).and_then(|t| make_date_ms(days, t))) {
|
||||
Some(ts) => NativeResult::Ok(JsValue::float(ts)),
|
||||
None => NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
@@ -626,6 +636,8 @@ pub fn date_set_time<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
let v = native_try!(date_arg_number(vm, val));
|
||||
// 新值过 TimeClip 包络,越界(含非有限)归 NaN。
|
||||
let v = time_clip(v);
|
||||
set_timestamp(obj, v);
|
||||
NativeResult::Ok(JsValue::float(v))
|
||||
}
|
||||
@@ -819,35 +831,52 @@ pub fn date_set_milliseconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCFullYear(y, m, d)`:设置 UTC 年份(可选月/日),返回新时间戳。
|
||||
///
|
||||
/// 无效 this 不早退:取 +0 基准(1970-01-01 UTC 全零)继续计算并写槽;
|
||||
/// 年/月/日参数在 this 值读取之后、NaN 判定之前逐一走完整 ToNumber。
|
||||
pub fn date_set_utc_full_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
// this 值先读,参数按序强转(规范步序),NaN 判定后置。
|
||||
let ms = get_timestamp(obj);
|
||||
let y = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let m = if args.len() > 2 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[2]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let d = if args.len() > 3 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[3]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !ms.is_finite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
// +0 基准:1970-01-01 UTC 全零,日内毫秒为 0。
|
||||
return apply_date_result!(obj, make_day(y, m.unwrap_or(0.0), d.unwrap_or(1.0)).and_then(|days| make_date_ms(days, 0)))
|
||||
}
|
||||
let ndt = match naive_from_ms(ms) {
|
||||
Some(n) => n,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let y = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let m = if args.len() > 2 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[2])))
|
||||
} else {
|
||||
ndt.date().month0() as f64
|
||||
};
|
||||
let d = if args.len() > 3 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[3])))
|
||||
} else {
|
||||
ndt.date().day() as f64
|
||||
};
|
||||
let m = m.unwrap_or(ndt.date().month0() as f64);
|
||||
let d = d.unwrap_or(ndt.date().day() as f64);
|
||||
let time_ms = utc_time_ms(&ndt);
|
||||
apply_date_result!(obj, make_day(y, m, d).and_then(|days| make_date_ms(days, time_ms)))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCMonth(m, d)`:设置 UTC 月份(可选日),返回新时间戳。
|
||||
///
|
||||
/// 参数在 this 值读取之后、NaN 判定之前逐一走完整 ToNumber;无效 this 在
|
||||
/// 强转完成后返回 NaN 且不写槽。
|
||||
pub fn date_set_utc_month<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
// this 值先读,参数按序强转(规范步序),NaN 判定后置。
|
||||
let ms = get_timestamp(obj);
|
||||
let m = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let d = if args.len() > 2 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[2]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !ms.is_finite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
@@ -855,20 +884,20 @@ pub fn date_set_utc_month<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Some(n) => n,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let m = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let d = if args.len() > 2 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[2])))
|
||||
} else {
|
||||
ndt.date().day() as f64
|
||||
};
|
||||
let d = d.unwrap_or(ndt.date().day() as f64);
|
||||
let time_ms = utc_time_ms(&ndt);
|
||||
apply_date_result!(obj, make_day(ndt.date().year() as f64, m, d).and_then(|days| make_date_ms(days, time_ms)))
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCDate(d)`:设置 UTC 日,返回新时间戳。
|
||||
///
|
||||
/// 参数在 this 值读取之后、NaN 判定之前走完整 ToNumber;无效 this 在强转
|
||||
/// 完成后返回 NaN 且不写槽。
|
||||
pub fn date_set_utc_date<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
// this 值先读,参数强转(规范步序),NaN 判定后置。
|
||||
let ms = get_timestamp(obj);
|
||||
let d = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
if !ms.is_finite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
@@ -876,7 +905,6 @@ pub fn date_set_utc_date<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Some(n) => n,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let d = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let time_ms = utc_time_ms(&ndt);
|
||||
apply_date_result!(
|
||||
obj,
|
||||
@@ -885,9 +913,29 @@ pub fn date_set_utc_date<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCHours(h, min, s, ms)`:设置 UTC 小时(可选分/秒/毫秒),返回新时间戳。
|
||||
///
|
||||
/// 参数在 this 值读取之后、NaN 判定之前逐一走完整 ToNumber;无效 this 在
|
||||
/// 强转完成后返回 NaN 且不写槽。
|
||||
pub fn date_set_utc_hours<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
// this 值先读,参数按序强转(规范步序),NaN 判定后置。
|
||||
let ms = get_timestamp(obj);
|
||||
let h = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let min = if args.len() > 2 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[2]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let sec = if args.len() > 3 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[3]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ms_arg = if args.len() > 4 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[4]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !ms.is_finite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
@@ -895,39 +943,34 @@ pub fn date_set_utc_hours<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Some(n) => n,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let h = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let min = if args.len() > 2 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[2])))
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let sec = if args.len() > 3 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[3])))
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let ms_arg = if args.len() > 4 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[4])))
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
// 缺省分量取当前 UTC 时刻值,与 MakeTime 组合后整体进位。
|
||||
let min_v = if args.len() > 2 { min } else { ndt.time().minute() as f64 };
|
||||
let sec_v = if args.len() > 3 { sec } else { ndt.time().second() as f64 };
|
||||
let ms_v = if args.len() > 4 {
|
||||
ms_arg
|
||||
} else {
|
||||
ndt.time().nanosecond() as f64 / 1_000_000.0
|
||||
};
|
||||
let min_v = min.unwrap_or(ndt.time().minute() as f64);
|
||||
let sec_v = sec.unwrap_or(ndt.time().second() as f64);
|
||||
let ms_v = ms_arg.unwrap_or(ndt.time().nanosecond() as f64 / 1_000_000.0);
|
||||
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
|
||||
let result = make_time(h, min_v, sec_v, ms_v).and_then(|time_ms| make_date_ms(days, time_ms));
|
||||
apply_date_result!(obj, result)
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCMinutes(min, s, ms)`:设置 UTC 分钟(可选秒/毫秒),返回新时间戳。
|
||||
///
|
||||
/// 参数在 this 值读取之后、NaN 判定之前逐一走完整 ToNumber;无效 this 在
|
||||
/// 强转完成后返回 NaN 且不写槽。
|
||||
pub fn date_set_utc_minutes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
// this 值先读,参数按序强转(规范步序),NaN 判定后置。
|
||||
let ms = get_timestamp(obj);
|
||||
let min = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let sec = if args.len() > 2 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[2]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ms_arg = if args.len() > 3 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[3]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !ms.is_finite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
@@ -935,32 +978,28 @@ pub fn date_set_utc_minutes<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
Some(n) => n,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let min = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let sec = if args.len() > 2 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[2])))
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let ms_arg = if args.len() > 3 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[3])))
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let sec_v = if args.len() > 2 { sec } else { ndt.time().second() as f64 };
|
||||
let ms_v = if args.len() > 3 {
|
||||
ms_arg
|
||||
} else {
|
||||
ndt.time().nanosecond() as f64 / 1_000_000.0
|
||||
};
|
||||
// 缺省分量取当前 UTC 时刻值,与 MakeTime 组合后整体进位。
|
||||
let sec_v = sec.unwrap_or(ndt.time().second() as f64);
|
||||
let ms_v = ms_arg.unwrap_or(ndt.time().nanosecond() as f64 / 1_000_000.0);
|
||||
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
|
||||
let result = make_time(ndt.time().hour() as f64, min, sec_v, ms_v).and_then(|time_ms| make_date_ms(days, time_ms));
|
||||
apply_date_result!(obj, result)
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCSeconds(s, ms)`:设置 UTC 秒(可选毫秒),返回新时间戳。
|
||||
///
|
||||
/// 参数在 this 值读取之后、NaN 判定之前逐一走完整 ToNumber;无效 this 在
|
||||
/// 强转完成后返回 NaN 且不写槽。
|
||||
pub fn date_set_utc_seconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
// this 值先读,参数按序强转(规范步序),NaN 判定后置。
|
||||
let ms = get_timestamp(obj);
|
||||
let sec = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let ms_arg = if args.len() > 2 {
|
||||
Some(native_try!(date_arg_number(vm, vm.reg(args[2]))))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if !ms.is_finite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
@@ -968,17 +1007,8 @@ pub fn date_set_utc_seconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
Some(n) => n,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let sec = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let ms_arg = if args.len() > 2 {
|
||||
native_try!(date_arg_number(vm, vm.reg(args[2])))
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let ms_v = if args.len() > 2 {
|
||||
ms_arg
|
||||
} else {
|
||||
ndt.time().nanosecond() as f64 / 1_000_000.0
|
||||
};
|
||||
// 缺省分量取当前 UTC 时刻值,与 MakeTime 组合后整体进位。
|
||||
let ms_v = ms_arg.unwrap_or(ndt.time().nanosecond() as f64 / 1_000_000.0);
|
||||
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
|
||||
let result = make_time(ndt.time().hour() as f64, ndt.time().minute() as f64, sec, ms_v)
|
||||
.and_then(|time_ms| make_date_ms(days, time_ms));
|
||||
@@ -986,9 +1016,14 @@ pub fn date_set_utc_seconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult
|
||||
}
|
||||
|
||||
/// `Date.prototype.setUTCMilliseconds(ms)`:设置 UTC 毫秒,返回新时间戳。
|
||||
///
|
||||
/// 参数在 this 值读取之后、NaN 判定之前走完整 ToNumber;无效 this 在强转
|
||||
/// 完成后返回 NaN 且不写槽。
|
||||
pub fn date_set_utc_milliseconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
|
||||
// this 值先读,参数强转(规范步序),NaN 判定后置。
|
||||
let ms = get_timestamp(obj);
|
||||
let ms_arg = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
if !ms.is_finite() {
|
||||
return NativeResult::Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
@@ -996,7 +1031,6 @@ pub fn date_set_utc_milliseconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRe
|
||||
Some(n) => n,
|
||||
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let ms_arg = native_try!(date_arg_number(vm, if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }));
|
||||
let days = civil_day_count(ndt.date().year() as i64, ndt.date().month() as i64, ndt.date().day() as i64);
|
||||
let result = make_time(ndt.time().hour() as f64, ndt.time().minute() as f64, ndt.time().second() as f64, ms_arg)
|
||||
.and_then(|time_ms| make_date_ms(days, time_ms));
|
||||
|
||||
@@ -496,11 +496,13 @@ fn date_set_utc_full_year_returns_timestamp() {
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
// setUTCFullYear 对无效 this 不早退:取 +0 基准(1970-01-01 UTC 全零)继续
|
||||
// 计算并写槽,年份 2025 落在 0..99 映射之外,结果为 2025-01-01T00:00:00Z。
|
||||
#[test]
|
||||
fn date_set_utc_invalid_returns_nan() {
|
||||
fn date_set_utc_full_year_invalid_this_uses_plus_zero_base() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "new Date(NaN).setUTCFullYear(2025)").unwrap();
|
||||
assert!(r.as_double().is_nan());
|
||||
assert_eq!(r.as_double(), 1735689600000.0);
|
||||
}
|
||||
|
||||
// setYear 无参数时不得索引缺失的参数寄存器(此前会 panic);
|
||||
@@ -640,3 +642,91 @@ fn date_length_descriptor() {
|
||||
.unwrap();
|
||||
assert!(r.as_bool());
|
||||
}
|
||||
|
||||
// UTC setter 族强转序钉:参数 ToNumber 先于 this 值 NaN 判定,无效 this 仍触发
|
||||
// valueOf(计数一次),返回 NaN 且不写槽。
|
||||
#[test]
|
||||
fn date_set_utc_month_invalid_this_still_coerces() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var d = new Date(NaN); var n = 0; \
|
||||
var v = d.setUTCMonth({ valueOf: function() { n++; return 0; } }); \
|
||||
n === 1 && Number.isNaN(v) && Number.isNaN(d.getTime()) ? 1 : 0",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
// setUTCFullYear 无效 this 取 +0 基准(1970-01-01 UTC 全零):
|
||||
// 未给月/日时取基准分量,结果为 1970-01-01T00:00:00Z 加 2025 年。
|
||||
#[test]
|
||||
fn date_set_utc_full_year_invalid_this_base_components() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var d = new Date(NaN); d.setUTCFullYear(1975, 2, 14) === new Date(Date.UTC(1975, 2, 14)).getTime() ? 1 : 0",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
// setTime 越界值经 TimeClip 归 NaN(|t| > 8.64e15 或非有限)。
|
||||
#[test]
|
||||
fn date_set_time_out_of_range_time_clip() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var d = new Date(0); var ts = d.setTime(8.64e15 + 1); \
|
||||
Number.isNaN(ts) && Number.isNaN(d.getTime()) ? 1 : 0",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
// 上界内的值原样入槽。
|
||||
let r = eval(&mut vm, "var d = new Date(0); d.setTime(8.64e15) === 8.64e15 ? 1 : 0").unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
// Date.UTC 年份先截断后做 0..99 映射:-0.999999 截断为 -0 落区间,归 1900 年。
|
||||
#[test]
|
||||
fn date_utc_truncates_before_year_offset() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(&mut vm, "Date.UTC(-0.999999, 0) === Date.UTC(1900, 0) ? 1 : 0").unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
let r = eval(&mut vm, "Date.UTC(99.999999, 0) === Date.UTC(1999, 0) ? 1 : 0").unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
let r = eval(&mut vm, "Date.UTC(100, 0) === -59011459200000 ? 1 : 0").unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
// 多参构造器七参按序走完整 ToNumber(对象参经 toString 通道),
|
||||
// 且某一参抛错时中断后续强转并原样传播异常。
|
||||
#[test]
|
||||
fn date_constructor_object_args_coercion_order() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var log = ''; \
|
||||
new Date({toString: function() { log += 'y'; return 0; }}, \
|
||||
{toString: function() { log += 'm'; return 0; }}, \
|
||||
{toString: function() { log += 'd'; return 1; }}); \
|
||||
log === 'ymd' ? 1 : 0",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(r.as_int(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_constructor_object_arg_error_propagates() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var n = 0; try { \
|
||||
new Date({ valueOf: function() { throw 'boom'; }}, \
|
||||
{ valueOf: function() { n++; } }); \
|
||||
'no'; \
|
||||
} catch (e) { e === 'boom' && n === 0 ? 'yes' : 'no' }",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(str_val(&vm, r), "yes");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user