feat: Date 严格 ISO 解析与 6 位年字符串输出

This commit is contained in:
2026-10-15 15:51:40 +00:00
parent f0b0dda887
commit d4d5b578de
14 changed files with 1012 additions and 266 deletions
+18 -18
View File
@@ -108,8 +108,8 @@ impl Vm {
// 注意:不能在此前置检查 BigInt 混合(`lv.is_bigint() || rv.is_bigint()`)——
// 对象操作数(如 `{valueOf: () => 2n}`)需先 ToPrimitive 再判定,混合检查
// 必须放在 coerce 之后。
let lhs = self.coerce_primitive_bounded(lv, false)?;
let rhs = self.coerce_primitive_bounded(rv, false)?;
let lhs = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
let rhs = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
reject_symbol_add!(self, lhs, rhs);
if lhs.is_string() || rhs.is_string() {
self.regs[rd] = self.concat_strings(lhs, rhs);
@@ -238,8 +238,8 @@ impl Vm {
}
// 快路径失败才 coerce:acc 已原语零副作用;next 对象 ToPrimitive 在此触发,
// 顺序与左结合 ADD 一致。
let lhs = self.coerce_primitive_bounded(acc, false)?;
let rhs = self.coerce_primitive_bounded(next, false)?;
let lhs = self.coerce_primitive_bounded(acc, coercion::ToPrimitiveHint::Number)?;
let rhs = self.coerce_primitive_bounded(next, coercion::ToPrimitiveHint::Number)?;
reject_symbol_add!(self, lhs, rhs);
if lhs.is_string() || rhs.is_string() {
// ── 阶段 2:字符串模式单趟预分配 ──
@@ -280,7 +280,7 @@ impl Vm {
parts.push(rhs);
for &reg in rest {
let v = self.regs[reg];
let prim = if v.is_object() { self.coerce_primitive_bounded(v, false)? } else { v };
let prim = if v.is_object() { self.coerce_primitive_bounded(v, coercion::ToPrimitiveHint::Number)? } else { v };
// 字符串模式对全部操作数做 ToString,Symbol 按 §7.1.17 抛 TypeError。
if prim.is_symbol() {
self.raise_type_error("Cannot convert a Symbol value to a string")?;
@@ -322,7 +322,7 @@ impl Vm {
let v = self.regs[a];
// ToPrimitive 先解盒(BigInt 包装对象如 Object(1n) 的 valueOf 返回 BigInt),
// 解盒后是 BigInt 才抛 TypeError——用户覆盖 valueOf/toString 的普通对象不受影响。
let prim = self.coerce_primitive_bounded(v, false)?;
let prim = self.coerce_primitive_bounded(v, coercion::ToPrimitiveHint::Number)?;
if prim.is_bigint() {
return self.raise_type_error("Cannot convert a BigInt value to a number");
}
@@ -348,8 +348,8 @@ impl Vm {
self.regs[rd] = self.new_bigint(self.bigint_value(lv) + self.bigint_value(rv));
return Ok(());
}
let lhs = self.coerce_primitive_bounded(lv, false)?;
let rhs = self.coerce_primitive_bounded(rv, false)?;
let lhs = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
let rhs = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
reject_symbol_add!(self, lhs, rhs);
if lhs.is_string() || rhs.is_string() {
self.regs[rd] = self.concat_strings(lhs, rhs);
@@ -387,8 +387,8 @@ impl Vm {
self.regs[rd] = self.new_bigint(self.bigint_value(lv) - self.bigint_value(rv));
return Ok(());
}
let l = self.coerce_primitive_bounded(lv, false)?;
let r = self.coerce_primitive_bounded(rv, false)?;
let l = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
let r = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
reject_symbol_number!(self, l, r);
if l.is_bigint() && r.is_bigint() {
self.regs[rd] = self.new_bigint(self.bigint_value(l) - self.bigint_value(r));
@@ -420,8 +420,8 @@ impl Vm {
self.regs[rd] = self.new_bigint(self.bigint_value(lv) * self.bigint_value(rv));
return Ok(());
}
let l = self.coerce_primitive_bounded(lv, false)?;
let r = self.coerce_primitive_bounded(rv, false)?;
let l = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
let r = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
reject_symbol_number!(self, l, r);
if l.is_bigint() && r.is_bigint() {
self.regs[rd] = self.new_bigint(self.bigint_value(l) * self.bigint_value(r));
@@ -457,8 +457,8 @@ impl Vm {
self.regs[rd] = self.new_bigint(self.bigint_value(lv) / r);
return Ok(());
}
let l = self.coerce_primitive_bounded(lv, false)?;
let r = self.coerce_primitive_bounded(rv, false)?;
let l = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
let r = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
reject_symbol_number!(self, l, r);
if l.is_bigint() && r.is_bigint() {
let rv = self.bigint_value(r);
@@ -506,8 +506,8 @@ impl Vm {
self.regs[rd] = self.new_bigint(self.bigint_value(lv) % r);
return Ok(());
}
let l = self.coerce_primitive_bounded(lv, false)?;
let r = self.coerce_primitive_bounded(rv, false)?;
let l = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
let r = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
reject_symbol_number!(self, l, r);
if l.is_bigint() && r.is_bigint() {
let rv = self.bigint_value(r);
@@ -551,8 +551,8 @@ impl Vm {
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)?;
let l = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
let r = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
if l.is_symbol() || r.is_symbol() {
self.raise_type_error("Cannot convert a Symbol value to a number")?;
return Ok(JsValue::undefined());
+4 -3
View File
@@ -2,6 +2,7 @@ use crate::vm::Vm;
use crate::vm_trace;
use num_bigint::{BigInt, Sign};
use num_traits::ToPrimitive;
use oxide_runtime_api as coercion;
use oxide_types::value::JsValue;
impl Vm {
@@ -19,13 +20,13 @@ impl Vm {
#[inline(always)]
fn coerce_primitive_pair(&mut self, lv: JsValue, rv: JsValue) -> Result<(JsValue, JsValue), String> {
let saved_pc = self.pc;
let l = self.coerce_primitive_bounded(lv, false)?;
let l = self.coerce_primitive_bounded(lv, coercion::ToPrimitiveHint::Number)?;
if self.pc != saved_pc {
// 转换异常已被外围 try/catch 接住:unwind 已改写 pc,立即中止后续转换,
// 由调用方在检测到 pc 变化后直接返回,避免继续用 undefined 计算结果并二次抛错。
return Ok((JsValue::undefined(), JsValue::undefined()));
}
let r = self.coerce_primitive_bounded(rv, false)?;
let r = self.coerce_primitive_bounded(rv, coercion::ToPrimitiveHint::Number)?;
if self.pc != saved_pc {
return Ok((JsValue::undefined(), JsValue::undefined()));
}
@@ -183,7 +184,7 @@ impl Vm {
pub(crate) fn dispatch_bit_not(&mut self, rd: usize, a: usize) -> Result<(), String> {
vm_trace!("BIT_NOT rd={} r{}", rd, a);
let saved_pc = self.pc;
let v = self.coerce_primitive_bounded(self.regs[a], false)?;
let v = self.coerce_primitive_bounded(self.regs[a], coercion::ToPrimitiveHint::Number)?;
if self.pc != saved_pc {
return Ok(());
}
+8 -8
View File
@@ -27,8 +27,8 @@ impl Vm {
#[inline(always)]
pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("LT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
let va = self.coerce_primitive_bounded(self.regs[a], coercion::ToPrimitiveHint::Number)?;
let vb = self.coerce_primitive_bounded(self.regs[b], coercion::ToPrimitiveHint::Number)?;
if va.is_symbol() || vb.is_symbol() {
return self.raise_type_error("Cannot convert a Symbol value to a number");
}
@@ -45,8 +45,8 @@ impl Vm {
#[inline(always)]
pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("GT rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
let va = self.coerce_primitive_bounded(self.regs[a], coercion::ToPrimitiveHint::Number)?;
let vb = self.coerce_primitive_bounded(self.regs[b], coercion::ToPrimitiveHint::Number)?;
if va.is_symbol() || vb.is_symbol() {
return self.raise_type_error("Cannot convert a Symbol value to a number");
}
@@ -63,8 +63,8 @@ impl Vm {
#[inline(always)]
pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("LTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
let va = self.coerce_primitive_bounded(self.regs[a], coercion::ToPrimitiveHint::Number)?;
let vb = self.coerce_primitive_bounded(self.regs[b], coercion::ToPrimitiveHint::Number)?;
if va.is_symbol() || vb.is_symbol() {
return self.raise_type_error("Cannot convert a Symbol value to a number");
}
@@ -81,8 +81,8 @@ impl Vm {
#[inline(always)]
pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
vm_trace!("GTE rd={} r{}={:?} r{}={:?}", rd, a, self.regs[a], b, self.regs[b]);
let va = self.coerce_primitive_bounded(self.regs[a], false)?;
let vb = self.coerce_primitive_bounded(self.regs[b], false)?;
let va = self.coerce_primitive_bounded(self.regs[a], coercion::ToPrimitiveHint::Number)?;
let vb = self.coerce_primitive_bounded(self.regs[b], coercion::ToPrimitiveHint::Number)?;
if va.is_symbol() || vb.is_symbol() {
return self.raise_type_error("Cannot convert a Symbol value to a number");
}
+2 -2
View File
@@ -191,8 +191,8 @@ impl Vm {
let obj = unsafe { &mut *obj_ptr };
self.read_member_prop(obj, prop_name_si, receiver)?
};
let prop_val = self.coerce_primitive_bounded(prop_raw, false)?;
let rhs = self.coerce_primitive_bounded(self.regs[a], false)?;
let prop_val = self.coerce_primitive_bounded(prop_raw, coercion::ToPrimitiveHint::Number)?;
let rhs = self.coerce_primitive_bounded(self.regs[a], coercion::ToPrimitiveHint::Number)?;
if prop_val.is_symbol() || rhs.is_symbol() {
// 复合成员加法分流同 `+`:字符串分支走 ToString、数值分支走 ToNumber,
// 两者对 Symbol 均抛 TypeError(ECMA-262 §7.1.4 / §7.1.17)。