feat: PlainMonthDay/PlainYearMonth 对象地基——构造器/getter/toString/toJSON 注册与 BuiltinWorld 接线

This commit is contained in:
2026-09-12 03:42:48 +00:00
parent 390b72e3bd
commit e33e7a1eb7
6 changed files with 834 additions and 6 deletions
+419 -2
View File
@@ -7,10 +7,13 @@ use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
// Temporal 命名空间的最小实现子集:Temporal.Now / Temporal.Instant /
// Temporal.PlainDate / Temporal.PlainTime / Temporal.PlainDateTime / Temporal.ZonedDateTime。
// Temporal.PlainDate / Temporal.PlainTime / Temporal.PlainDateTime /
// Temporal.PlainMonthDay / Temporal.PlainYearMonth / Temporal.ZonedDateTime。
// 内部数据按对象类型存入 prop 槽:
// Instant 存纪元纳秒(BigInt,prop 0)、PlainDate 存年/月/日/日历 ID(prop 0-3)、
// PlainTime 存午夜后纳秒(f64,prop 0)、PlainDateTime 存年/月/日/午夜后纳秒/日历 ID(prop 0-4),
// PlainTime 存午夜后纳秒(f64,prop 0)、PlainDateTime 存年/月/日/午夜后纳秒/日历 ID(prop 0-4)、
// PlainMonthDay 存月/日/参考年/日历 ID(prop 0-3)、
// PlainYearMonth 存年/月/参考日/日历 ID(prop 0-3)、
// ZonedDateTime 存纪元纳秒、时区 ID、日历 ID(prop 0-2)。
// 日历 ID 未显式给定时统一取 "iso8601"。
@@ -2089,6 +2092,8 @@ fn reject_partial_object_with_calendar_or_time_zone<H: VmHost>(vm: &mut H, value
|| obj.is_plain_date_time_obj()
|| obj.is_plain_time_obj()
|| obj.is_zoned_date_time_obj()
|| obj.is_plain_month_day_obj()
|| obj.is_plain_year_month_obj()
{
return Err(crate::error::create_type_error(vm, "invalid argument"));
}
@@ -5563,6 +5568,9 @@ fn temporal_calendar_id<H: VmHost>(vm: &mut H, value: JsValue) -> Result<Option<
if obj.is_zoned_date_time_obj() {
return Ok(Some(get_calendar_id(obj, 2)));
}
if obj.is_plain_month_day_obj() || obj.is_plain_year_month_obj() {
return Ok(Some(get_calendar_id(obj, 3)));
}
}
}
Err(crate::error::create_type_error(vm, "invalid calendar"))
@@ -7483,6 +7491,415 @@ pub fn plain_date_since<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
)
}
// Temporal.PlainMonthDay / Temporal.PlainYearMonth 对象地基:
// 数字分量转换、槽对象构造、构造器与 getter/toString/toJSON。
/// Temporal 数字分量转换(number-only 路径):先 ToPrimitive(Number)——对象经
/// valueOf/toString 取值;Symbol/BigInt → TypeError;NaN/±Inf → RangeError
/// (undefined 与不可解析串都归 NaN,由此统一抛 RangeError);其余截断取整。
fn temporal_number_component<H: VmHost>(vm: &mut H, value: JsValue) -> Result<f64, JsValue> {
let primitive = oxide_runtime_api::to_primitive(value, oxide_runtime_api::ToPrimitiveHint::Number, vm)
.map_err(|error| native_engine_error(vm, &error))?;
if primitive.is_symbol() || primitive.is_bigint() {
return Err(crate::error::create_type_error(vm, "invalid number"));
}
let number = to_number(primitive);
if number.is_infinite() || number.is_nan() {
return Err(crate::error::create_range_error(vm, "invalid number"));
}
Ok(number.trunc())
}
/// ISO 年-月是否落在 PlainYearMonth 可表示范围(ISOYearMonthWithinLimits):
/// 仅 -271821 年 3 月及以前、+275760 年 10 月及以后越界。
fn iso_year_month_within_limits(year: i32, month: u32) -> bool {
!((year == -271821 && month < 4) || (year == 275760 && month > 9))
}
/// 构造 PlainMonthDay 实例对象(槽 0-3 = 月/日/参考年/日历 ID)。
/// from/toPlainDate 等返回新对象的成员使用;构造器走 receiver 初始化路径。
#[expect(dead_code)]
fn make_plain_month_day<H: VmHost>(vm: &mut H, month: u32, day: u32, ref_year: i32, calendar: &str) -> NativeResult {
let proto = JsValue::from_js_object(vm.session().builtin_world().plain_month_day_proto.as_ptr() as *mut JsObject);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto);
obj.type_tag = JsObject::OBJ_TYPE_PLAIN_MONTH_DAY;
obj.set_prop_at(0, JsValue::float(month as f64));
obj.set_prop_at(1, JsValue::float(day as f64));
obj.set_prop_at(2, JsValue::float(ref_year as f64));
obj.set_prop_at(3, vm.new_string(calendar));
NativeResult::Ok(JsValue::from_js_object(vm.alloc_object(obj)))
}
/// 构造 PlainYearMonth 实例对象(槽 0-3 = 年/月/参考日/日历 ID)。
/// from/toPlainDate 等返回新对象的成员使用;构造器走 receiver 初始化路径。
#[expect(dead_code)]
fn make_plain_year_month<H: VmHost>(vm: &mut H, year: i32, month: u32, ref_day: u32, calendar: &str) -> NativeResult {
let proto = JsValue::from_js_object(vm.session().builtin_world().plain_year_month_proto.as_ptr() as *mut JsObject);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto);
obj.type_tag = JsObject::OBJ_TYPE_PLAIN_YEAR_MONTH;
obj.set_prop_at(0, JsValue::float(year as f64));
obj.set_prop_at(1, JsValue::float(month as f64));
obj.set_prop_at(2, JsValue::float(ref_day as f64));
obj.set_prop_at(3, vm.new_string(calendar));
NativeResult::Ok(JsValue::from_js_object(vm.alloc_object(obj)))
}
fn ensure_plain_month_day<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<(), JsValue> {
if !obj.is_plain_month_day_obj() {
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
}
Ok(())
}
fn ensure_plain_year_month<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<(), JsValue> {
if !obj.is_plain_year_month_obj() {
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
}
Ok(())
}
/// 读 PlainMonthDay 的 月/日/参考年 三元组(槽 0-2),含 receiver 校验。
fn plain_month_day_mdy<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<(f64, f64, f64), JsValue> {
let ptr = receiver_obj(vm, args)?;
let obj = unsafe { &*ptr };
ensure_plain_month_day(vm, obj)?;
Ok((get_double_prop(obj, 0), get_double_prop(obj, 1), get_double_prop(obj, 2)))
}
/// 读 PlainYearMonth 的 年/月/参考日 三元组(槽 0-2),含 receiver 校验。
fn plain_year_month_ymd<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<(f64, f64, f64), JsValue> {
let ptr = receiver_obj(vm, args)?;
let obj = unsafe { &*ptr };
ensure_plain_year_month(vm, obj)?;
Ok((get_double_prop(obj, 0), get_double_prop(obj, 1), get_double_prop(obj, 2)))
}
/// 构造器日历参数解析(ToTemporalCalendarSlotValue,缺省 "iso8601"):
/// undefined → 缺省;字符串 → 严格白名单(非法 RangeError);Temporal 日期实例 →
/// 日历槽直读(不触发属性 getter);函数对象 → 缺省(无日历行为);
/// 其他对象与原始值 → TypeError。
fn temporal_constructor_calendar_id<H: VmHost>(vm: &mut H, value: JsValue) -> Result<String, JsValue> {
if value.is_undefined() {
return Ok("iso8601".to_string());
}
if value.is_string() {
return temporal_calendar_id_strict(vm, value)
.map(|calendar| calendar.unwrap_or_else(|| "iso8601".to_string()));
}
if value.is_object() {
let ptr = value.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
if obj.is_function() {
return Ok("iso8601".to_string());
}
let slot = if obj.is_plain_date_obj() {
3
} else if obj.is_plain_date_time_obj() {
4
} else if obj.is_zoned_date_time_obj() {
2
} else if obj.is_plain_month_day_obj() || obj.is_plain_year_month_obj() {
3
} else {
return Err(crate::error::create_type_error(vm, "invalid calendar"));
};
return Ok(get_calendar_id(obj, slot));
}
}
Err(crate::error::create_type_error(vm, "invalid calendar"))
}
/// `Temporal.PlainMonthDay` 构造器:`new PlainMonthDay(month, day[, calendar[, referenceISOYear]])`。
pub fn plain_month_day_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ctor_proto = vm.session().builtin_world().plain_month_day_proto.as_ptr();
if !is_ctor_call(vm, args, ctor_proto) {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Class constructor Temporal.PlainMonthDay cannot be invoked without 'new'",
));
}
// 转换序:month → day → calendar → referenceISOYear(后两者有缺省值)。
let month = native_try!(temporal_number_component(
vm,
if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() },
));
let day = native_try!(temporal_number_component(
vm,
if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() },
));
let calendar_raw = if args.len() > 3 { vm.reg(args[3]) } else { JsValue::undefined() };
let calendar = native_try!(temporal_constructor_calendar_id(vm, calendar_raw));
let ref_year = if args.len() > 4 && !vm.reg(args[4]).is_undefined() {
native_try!(temporal_number_component(vm, vm.reg(args[4]))) as i32
} else {
1972
};
if !valid_iso_date(ref_year, month as u32, day as u32) {
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ISO date"));
}
// 表示范围(ISODateWithinLimits):-271821-04-19 … +275760-09-13。
let day_count = days_from_civil(i128::from(ref_year), i128::from(month as i32), i128::from(day as i32));
if !(-100_000_001..=100_000_000).contains(&day_count) {
return NativeResult::Err(crate::error::create_range_error(vm, "ISO date is out of range"));
}
let calendar_value = vm.new_string(&calendar);
initialize_temporal_receiver(
vm,
args,
JsObject::OBJ_TYPE_PLAIN_MONTH_DAY,
[
JsValue::float(month),
JsValue::float(day),
JsValue::float(ref_year as f64),
calendar_value,
],
)
}
/// `Temporal.PlainYearMonth` 构造器:`new PlainYearMonth(year, month[, calendar[, referenceISODay]])`。
pub fn plain_year_month_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ctor_proto = vm.session().builtin_world().plain_year_month_proto.as_ptr();
if !is_ctor_call(vm, args, ctor_proto) {
return NativeResult::Err(crate::error::create_type_error(
vm,
"Class constructor Temporal.PlainYearMonth cannot be invoked without 'new'",
));
}
// 转换序:year → month → calendar → referenceISODay(后两者有缺省值)。
let year = native_try!(temporal_number_component(
vm,
if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() },
)) as i32;
let month = native_try!(temporal_number_component(
vm,
if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() },
));
let calendar_raw = if args.len() > 3 { vm.reg(args[3]) } else { JsValue::undefined() };
let calendar = native_try!(temporal_constructor_calendar_id(vm, calendar_raw));
let ref_day = if args.len() > 4 && !vm.reg(args[4]).is_undefined() {
native_try!(temporal_number_component(vm, vm.reg(args[4])))
} else {
1.0
};
let month_i = month as i32;
if !(1..=12).contains(&month_i) {
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ISO date"));
}
let ref_day_i = ref_day as i32;
if ref_day_i < 1 || ref_day_i > days_in_month_iso(year, month_i as u32) as i32 {
return NativeResult::Err(crate::error::create_range_error(vm, "invalid ISO date"));
}
if !iso_year_month_within_limits(year, month_i as u32) {
return NativeResult::Err(crate::error::create_range_error(vm, "ISO date is out of range"));
}
let calendar_value = vm.new_string(&calendar);
initialize_temporal_receiver(
vm,
args,
JsObject::OBJ_TYPE_PLAIN_YEAR_MONTH,
[
JsValue::float(year as f64),
JsValue::float(month),
JsValue::float(ref_day),
calendar_value,
],
)
}
/// `Temporal.PlainMonthDay.prototype.day` getter(槽 1)。
pub fn plain_month_day_day<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (_, day, _) = native_try!(plain_month_day_mdy(vm, args));
NativeResult::Ok(JsValue::float(day))
}
/// `Temporal.PlainMonthDay.prototype.monthCode` getter:`M01`..`M12`。
pub fn plain_month_day_month_code<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (month, _, _) = native_try!(plain_month_day_mdy(vm, args));
NativeResult::Ok(vm.new_string(&format!("M{:02}", month as u32)))
}
/// `Temporal.PlainMonthDay.prototype.calendarId` getter:读日历槽(槽 3)。
pub fn plain_month_day_calendar_id<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ptr = native_try!(receiver_obj(vm, args));
let obj = unsafe { &*ptr };
native_try!(ensure_plain_month_day(vm, obj));
NativeResult::Ok(vm.new_string(&get_calendar_id(obj, 3)))
}
/// `Temporal.PlainYearMonth.prototype.year` getter(槽 0)。
pub fn plain_year_month_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (year, _, _) = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::float(year))
}
/// `Temporal.PlainYearMonth.prototype.month` getter(槽 1)。
pub fn plain_year_month_month<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (_, month, _) = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::float(month))
}
/// `Temporal.PlainYearMonth.prototype.monthCode` getter:`M01`..`M12`。
pub fn plain_year_month_month_code<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (_, month, _) = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(vm.new_string(&format!("M{:02}", month as u32)))
}
/// `Temporal.PlainYearMonth.prototype.calendarId` getter:读日历槽(槽 3)。
pub fn plain_year_month_calendar_id<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ptr = native_try!(receiver_obj(vm, args));
let obj = unsafe { &*ptr };
native_try!(ensure_plain_year_month(vm, obj));
NativeResult::Ok(vm.new_string(&get_calendar_id(obj, 3)))
}
/// `Temporal.PlainYearMonth.prototype.daysInMonth` getter:按年月的 ISO 月长。
pub fn plain_year_month_days_in_month<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (year, month, _) = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::float(days_in_month_iso(year as i32, month as u32) as f64))
}
/// `Temporal.PlainYearMonth.prototype.daysInYear` getter:366(闰年)或 365。
pub fn plain_year_month_days_in_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (year, _, _) = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::float(if is_leap_year_iso(year as i32) { 366.0 } else { 365.0 }))
}
/// `Temporal.PlainYearMonth.prototype.monthsInYear` getter:恒 12(ISO 日历)。
pub fn plain_year_month_months_in_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let _ = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::float(12.0))
}
/// `Temporal.PlainYearMonth.prototype.inLeapYear` getter。
pub fn plain_year_month_in_leap_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let (year, _, _) = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::bool(is_leap_year_iso(year as i32)))
}
/// `Temporal.PlainYearMonth.prototype.era` getter:ISO 日历无纪元,恒 undefined。
pub fn plain_year_month_era<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let _ = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::undefined())
}
/// `Temporal.PlainYearMonth.prototype.eraYear` getter:ISO 日历无纪元,恒 undefined。
pub fn plain_year_month_era_year<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let _ = native_try!(plain_year_month_ymd(vm, args));
NativeResult::Ok(JsValue::undefined())
}
/// toString 的 calendarName 选项取值(auto/never 同默认形)。
enum ShowCalendar {
Omitted,
Always,
Critical,
}
/// 读并校验 toString 的 calendarName 选项:options 非对象 → TypeError;
/// 值非字符串先 ToPrimitive(String) 转换(Symbol → TypeError);
/// 仅 auto/always/never/critical 合法(大小写敏感),否则 RangeError。
fn temporal_to_show_calendar<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<ShowCalendar, JsValue> {
let options = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if options.is_undefined() {
return Ok(ShowCalendar::Omitted);
}
if !options.is_object() {
return Err(crate::error::create_type_error(vm, "options must be an object"));
}
let ptr = options.as_js_object_ptr();
if ptr.is_null() {
return Err(crate::error::create_type_error(vm, "options must be an object"));
}
let raw = temporal_option_value(vm, unsafe { &*ptr }, options, "calendarName")?;
if raw.is_undefined() {
return Ok(ShowCalendar::Omitted);
}
let value = temporal_option_string(vm, raw)?;
match value.as_str() {
"always" => Ok(ShowCalendar::Always),
"critical" => Ok(ShowCalendar::Critical),
"auto" | "never" => Ok(ShowCalendar::Omitted),
_ => Err(crate::error::create_range_error(vm, "invalid calendarName")),
}
}
/// PlainMonthDay 默认形串(`MM-DD`),含 receiver 校验,不读 options。
fn plain_month_day_default_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ptr = native_try!(receiver_obj(vm, args));
let obj = unsafe { &*ptr };
native_try!(ensure_plain_month_day(vm, obj));
let month = get_double_prop(obj, 0) as i32;
let day = get_double_prop(obj, 1) as i32;
NativeResult::Ok(vm.new_string_owned(format!("{month:02}-{day:02}")))
}
/// `Temporal.PlainMonthDay.prototype.toString([options])`:
/// 默认 `MM-DD`;always/critical 补参考年与 `[u-ca=…]`/`[!u-ca=…]` 注解。
pub fn plain_month_day_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ptr = native_try!(receiver_obj(vm, args));
let obj = unsafe { &*ptr };
native_try!(ensure_plain_month_day(vm, obj));
let show = native_try!(temporal_to_show_calendar(vm, args));
if matches!(show, ShowCalendar::Omitted) {
return plain_month_day_default_string(vm, args);
}
let month = get_double_prop(obj, 0) as i32;
let day = get_double_prop(obj, 1) as i32;
let ref_year = get_double_prop(obj, 2) as i32;
let calendar = get_calendar_id(obj, 3);
let annotation = match show {
ShowCalendar::Always => format!("[u-ca={calendar}]"),
_ => format!("[!u-ca={calendar}]"),
};
NativeResult::Ok(
vm.new_string_owned(format!("{}-{month:02}-{day:02}{annotation}", format_iso_year(ref_year as i128))),
)
}
/// `Temporal.PlainMonthDay.prototype.toJSON()`:默认形串,忽略参数。
pub fn plain_month_day_to_json<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
plain_month_day_default_string(vm, args)
}
/// PlainYearMonth 默认形串(`±YYYY-MM`),含 receiver 校验,不读 options。
fn plain_year_month_default_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ptr = native_try!(receiver_obj(vm, args));
let obj = unsafe { &*ptr };
native_try!(ensure_plain_year_month(vm, obj));
let year = get_double_prop(obj, 0) as i32;
let month = get_double_prop(obj, 1) as i32;
NativeResult::Ok(vm.new_string_owned(format!("{}-{month:02}", format_iso_year(year as i128))))
}
/// `Temporal.PlainYearMonth.prototype.toString([options])`:
/// 默认 `±YYYY-MM`;always/critical 补参考日与 `[u-ca=…]`/`[!u-ca=…]` 注解。
pub fn plain_year_month_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
let ptr = native_try!(receiver_obj(vm, args));
let obj = unsafe { &*ptr };
native_try!(ensure_plain_year_month(vm, obj));
let show = native_try!(temporal_to_show_calendar(vm, args));
if matches!(show, ShowCalendar::Omitted) {
return plain_year_month_default_string(vm, args);
}
let year = get_double_prop(obj, 0) as i32;
let month = get_double_prop(obj, 1) as i32;
let ref_day = get_double_prop(obj, 2) as i32;
let calendar = get_calendar_id(obj, 3);
let annotation = match show {
ShowCalendar::Always => format!("[u-ca={calendar}]"),
_ => format!("[!u-ca={calendar}]"),
};
NativeResult::Ok(
vm.new_string_owned(format!("{}-{month:02}-{ref_day:02}{annotation}", format_iso_year(year as i128))),
)
}
/// `Temporal.PlainYearMonth.prototype.toJSON()`:默认形串,忽略参数。
pub fn plain_year_month_to_json<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
plain_year_month_default_string(vm, args)
}
#[cfg(test)]
mod tests {
use super::*;
+48 -2
View File
@@ -285,6 +285,10 @@ pub struct BuiltinWorld {
pub zoned_date_time_proto: P<JsObject>,
pub plain_date_time_constructor: P<JsObject>,
pub plain_date_time_proto: P<JsObject>,
pub plain_month_day_constructor: P<JsObject>,
pub plain_month_day_proto: P<JsObject>,
pub plain_year_month_constructor: P<JsObject>,
pub plain_year_month_proto: P<JsObject>,
pub bigint_constructor: P<JsObject>,
pub bigint_proto: P<JsObject>,
/// `%IteratorPrototype%`:各集合迭代器原型的公共祖先,持有 `@@iterator`(返回自身)。
@@ -600,6 +604,8 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
wire_ctor_proto(&world.duration_constructor, &world.duration_proto);
wire_ctor_proto(&world.zoned_date_time_constructor, &world.zoned_date_time_proto);
wire_ctor_proto(&world.plain_date_time_constructor, &world.plain_date_time_proto);
wire_ctor_proto(&world.plain_month_day_constructor, &world.plain_month_day_proto);
wire_ctor_proto(&world.plain_year_month_constructor, &world.plain_year_month_proto);
wire_ctor_proto(&world.bigint_constructor, &world.bigint_proto);
// 所有内置构造器的 [[Prototype]] 指向 %FunctionPrototype%(ECMA-262 §17:
@@ -627,12 +633,14 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
&world.duration_constructor,
&world.zoned_date_time_constructor,
&world.plain_date_time_constructor,
&world.plain_month_day_constructor,
&world.plain_year_month_constructor,
] {
set_proto_if_changed(ctor, fn_proto_val);
}
let obj_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
let non_object_protos: [&P<JsObject>; 23] = [
let non_object_protos: [&P<JsObject>; 25] = [
&world.array_proto,
&world.function_proto,
&world.string_proto,
@@ -653,6 +661,8 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
&world.duration_proto,
&world.zoned_date_time_proto,
&world.plain_date_time_proto,
&world.plain_month_day_proto,
&world.plain_year_month_proto,
&world.bigint_proto,
&world.disposable_stack_proto,
&world.async_disposable_stack_proto,
@@ -911,7 +921,7 @@ impl BuiltinWorld {
/// session 收尾(`teardown_heap_data`)与选择性重建收尾(`retire_replaced`)
/// 的 P 字段枚举唯一入口:`BuiltinWorld` 新增 P 字段须在此同步补一行,否则
/// 收尾时该字段属性区永久泄漏、重建重指/释放漏掉该字段。
pub(crate) fn all_p_fields(&self) -> [&P<JsObject>; 100] {
pub(crate) fn all_p_fields(&self) -> [&P<JsObject>; 104] {
[
&self.object_proto,
&self.array_proto,
@@ -1001,6 +1011,10 @@ impl BuiltinWorld {
&self.zoned_date_time_proto,
&self.plain_date_time_constructor,
&self.plain_date_time_proto,
&self.plain_month_day_constructor,
&self.plain_month_day_proto,
&self.plain_year_month_constructor,
&self.plain_year_month_proto,
&self.bigint_constructor,
&self.bigint_proto,
&self.iterator_proto,
@@ -1148,6 +1162,10 @@ impl BuiltinWorld {
BuiltinId::ZonedDateTimeProto => &self.zoned_date_time_proto,
BuiltinId::PlainDateTimeConstructor => &self.plain_date_time_constructor,
BuiltinId::PlainDateTimeProto => &self.plain_date_time_proto,
BuiltinId::PlainMonthDayConstructor => &self.plain_month_day_constructor,
BuiltinId::PlainMonthDayProto => &self.plain_month_day_proto,
BuiltinId::PlainYearMonthConstructor => &self.plain_year_month_constructor,
BuiltinId::PlainYearMonthProto => &self.plain_year_month_proto,
BuiltinId::BigIntConstructor => &self.bigint_constructor,
BuiltinId::BigIntProto => &self.bigint_proto,
BuiltinId::Console => &self.console_object,
@@ -1210,6 +1228,10 @@ impl BuiltinWorld {
make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime");
let (plain_date_time_proto, plain_date_time_constructor) =
make_named_pair(string_forge, shape_forge, labels, "PlainDateTime");
let (plain_month_day_proto, plain_month_day_constructor) =
make_named_pair(string_forge, shape_forge, labels, "PlainMonthDay");
let (plain_year_month_proto, plain_year_month_constructor) =
make_named_pair(string_forge, shape_forge, labels, "PlainYearMonth");
let (bigint_proto, bigint_constructor) = make_named_pair(string_forge, shape_forge, labels, "BigInt");
let stub_objects = Vec::new();
@@ -1314,6 +1336,10 @@ impl BuiltinWorld {
zoned_date_time_proto,
plain_date_time_constructor,
plain_date_time_proto,
plain_month_day_constructor,
plain_month_day_proto,
plain_year_month_constructor,
plain_year_month_proto,
bigint_constructor,
bigint_proto,
iterator_proto,
@@ -1548,6 +1574,10 @@ impl BuiltinWorld {
zoned_date_time_constructor,
plain_date_time_proto,
plain_date_time_constructor,
plain_month_day_proto,
plain_month_day_constructor,
plain_year_month_proto,
plain_year_month_constructor,
) = if dirty.temporal {
let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant");
let (plain_date_proto, plain_date_constructor) =
@@ -1559,6 +1589,10 @@ impl BuiltinWorld {
make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime");
let (plain_date_time_proto, plain_date_time_constructor) =
make_named_pair(string_forge, shape_forge, labels, "PlainDateTime");
let (plain_month_day_proto, plain_month_day_constructor) =
make_named_pair(string_forge, shape_forge, labels, "PlainMonthDay");
let (plain_year_month_proto, plain_year_month_constructor) =
make_named_pair(string_forge, shape_forge, labels, "PlainYearMonth");
(
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
@@ -1574,6 +1608,10 @@ impl BuiltinWorld {
zoned_date_time_constructor,
plain_date_time_proto,
plain_date_time_constructor,
plain_month_day_proto,
plain_month_day_constructor,
plain_year_month_proto,
plain_year_month_constructor,
)
} else {
(
@@ -1591,6 +1629,10 @@ impl BuiltinWorld {
current.zoned_date_time_constructor.clone(),
current.plain_date_time_proto.clone(),
current.plain_date_time_constructor.clone(),
current.plain_month_day_proto.clone(),
current.plain_month_day_constructor.clone(),
current.plain_year_month_proto.clone(),
current.plain_year_month_constructor.clone(),
)
};
let (bigint_proto, bigint_constructor) = if dirty.stubs {
@@ -1732,6 +1774,10 @@ impl BuiltinWorld {
zoned_date_time_proto,
plain_date_time_constructor,
plain_date_time_proto,
plain_month_day_constructor,
plain_month_day_proto,
plain_year_month_constructor,
plain_year_month_proto,
bigint_constructor,
bigint_proto,
iterator_proto,
+14 -2
View File
@@ -318,7 +318,7 @@ pub struct KernelSession {
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
/// builtin 家族。
pub const NUM_BUILTINS: usize = 90;
pub const NUM_BUILTINS: usize = 94;
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
///
@@ -418,6 +418,10 @@ pub enum BuiltinId {
SymDispose = 87,
SuppressedErrorProto = 88,
Console = 89,
PlainMonthDayConstructor = 90,
PlainMonthDayProto = 91,
PlainYearMonthConstructor = 92,
PlainYearMonthProto = 93,
}
impl BuiltinId {
@@ -515,6 +519,10 @@ impl BuiltinId {
BuiltinId::SymDispose,
BuiltinId::SuppressedErrorProto,
BuiltinId::Console,
BuiltinId::PlainMonthDayConstructor,
BuiltinId::PlainMonthDayProto,
BuiltinId::PlainYearMonthConstructor,
BuiltinId::PlainYearMonthProto,
];
}
@@ -776,7 +784,11 @@ impl KernelSession {
|| gen(BuiltinId::ZonedDateTimeConstructor) != snap(BuiltinId::ZonedDateTimeConstructor)
|| gen(BuiltinId::ZonedDateTimeProto) != snap(BuiltinId::ZonedDateTimeProto)
|| gen(BuiltinId::PlainDateTimeConstructor) != snap(BuiltinId::PlainDateTimeConstructor)
|| gen(BuiltinId::PlainDateTimeProto) != snap(BuiltinId::PlainDateTimeProto),
|| gen(BuiltinId::PlainDateTimeProto) != snap(BuiltinId::PlainDateTimeProto)
|| gen(BuiltinId::PlainMonthDayConstructor) != snap(BuiltinId::PlainMonthDayConstructor)
|| gen(BuiltinId::PlainMonthDayProto) != snap(BuiltinId::PlainMonthDayProto)
|| gen(BuiltinId::PlainYearMonthConstructor) != snap(BuiltinId::PlainYearMonthConstructor)
|| gen(BuiltinId::PlainYearMonthProto) != snap(BuiltinId::PlainYearMonthProto),
stubs: world.stub_objects.len() != snapshot.stub_objects_len
|| stub_generations_dirty
|| gen(BuiltinId::BigIntConstructor) != snap(BuiltinId::BigIntConstructor)
+14
View File
@@ -661,6 +661,10 @@ impl JsObject {
pub const OBJ_TYPE_DISPOSABLE_STACK: u8 = 24;
/// AsyncDisposableStack 对象(异步资源栈):状态盒与同步栈同构,预留 type_tag。
pub const OBJ_TYPE_ASYNC_DISPOSABLE_STACK: u8 = 25;
/// Temporal.PlainMonthDay 对象:月/日/参考年/日历 ID 存于 prop 0-3。
pub const OBJ_TYPE_PLAIN_MONTH_DAY: u8 = 26;
/// Temporal.PlainYearMonth 对象:年/月/参考日/日历 ID 存于 prop 0-3。
pub const OBJ_TYPE_PLAIN_YEAR_MONTH: u8 = 27;
/// `is_session_epoch` 字段中的 session 标记位。
pub const SESSION_EPOCH_BIT: u8 = 0x01;
/// `is_session_epoch` 字段中的 GC 标记位。
@@ -764,6 +768,16 @@ impl JsObject {
pub fn is_plain_date_time_obj(&self) -> bool {
self.type_tag == Self::OBJ_TYPE_PLAIN_DATE_TIME
}
/// 是否 Temporal.PlainMonthDay 对象。
#[inline]
pub fn is_plain_month_day_obj(&self) -> bool {
self.type_tag == Self::OBJ_TYPE_PLAIN_MONTH_DAY
}
/// 是否 Temporal.PlainYearMonth 对象。
#[inline]
pub fn is_plain_year_month_obj(&self) -> bool {
self.type_tag == Self::OBJ_TYPE_PLAIN_YEAR_MONTH
}
#[inline]
pub fn is_arguments_obj(&self) -> bool {
@@ -957,6 +957,150 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
PropAttributes::new(false, false, true),
);
// Temporal.PlainMonthDay:构造器、3 个分量 getter 与 toString/toJSON。
let plain_month_day_ctor_ptr = world.plain_month_day_constructor.as_ptr() as *mut JsObject;
let plain_month_day_ctor = unsafe { &mut *plain_month_day_ctor_ptr };
configure_native_constructor(
plain_month_day_ctor,
oxide_builtins::temporal::plain_month_day_constructor::<crate::vm::Vm> as *const (),
2,
);
let length_si = core.perm_interner().intern("length").0;
let length_shape = core.shape_forge().make_shape(plain_month_day_ctor.shape_id(), length_si);
plain_month_day_ctor.set_shape_id(length_shape);
plain_month_day_ctor.ensure_hash_props().push(JsValue::int(2));
let length_pos = plain_month_day_ctor
.hash_props_vec()
.map_or(0, |props| props.len() as u32)
.saturating_sub(1);
plain_month_day_ctor.set_data_meta(length_pos, PropAttributes::new(false, false, true));
let plain_month_day_proto_ptr = world.plain_month_day_proto.as_ptr() as *mut JsObject;
let plain_month_day_proto = unsafe { &mut *plain_month_day_proto_ptr };
for (name, getter) in [
("day", oxide_builtins::temporal::plain_month_day_day::<crate::vm::Vm> as *const ()),
(
"monthCode",
oxide_builtins::temporal::plain_month_day_month_code::<crate::vm::Vm> as *const (),
),
(
"calendarId",
oxide_builtins::temporal::plain_month_day_calendar_id::<crate::vm::Vm> as *const (),
),
] {
bind_accessor_getter(core, session, plain_month_day_proto, name, getter);
}
apply_binding_table(
world,
plain_month_day_proto,
core,
&[
(
"toString",
oxide_builtins::temporal::plain_month_day_to_string::<crate::vm::Vm> as *const (),
0,
),
(
"toJSON",
oxide_builtins::temporal::plain_month_day_to_json::<crate::vm::Vm> as *const (),
0,
),
],
);
bind_well_known_data_property(
core,
plain_month_day_proto,
9,
JsValue::perm_string(
core.perm_interner()
.string_ptr(core.perm_interner().intern("Temporal.PlainMonthDay").0),
),
PropAttributes::new(false, false, true),
);
// Temporal.PlainYearMonth:构造器、10 个 getter 与 toString/toJSON。
let plain_year_month_ctor_ptr = world.plain_year_month_constructor.as_ptr() as *mut JsObject;
let plain_year_month_ctor = unsafe { &mut *plain_year_month_ctor_ptr };
configure_native_constructor(
plain_year_month_ctor,
oxide_builtins::temporal::plain_year_month_constructor::<crate::vm::Vm> as *const (),
2,
);
let length_si = core.perm_interner().intern("length").0;
let length_shape = core.shape_forge().make_shape(plain_year_month_ctor.shape_id(), length_si);
plain_year_month_ctor.set_shape_id(length_shape);
plain_year_month_ctor.ensure_hash_props().push(JsValue::int(2));
let length_pos = plain_year_month_ctor
.hash_props_vec()
.map_or(0, |props| props.len() as u32)
.saturating_sub(1);
plain_year_month_ctor.set_data_meta(length_pos, PropAttributes::new(false, false, true));
let plain_year_month_proto_ptr = world.plain_year_month_proto.as_ptr() as *mut JsObject;
let plain_year_month_proto = unsafe { &mut *plain_year_month_proto_ptr };
for (name, getter) in [
("year", oxide_builtins::temporal::plain_year_month_year::<crate::vm::Vm> as *const ()),
("month", oxide_builtins::temporal::plain_year_month_month::<crate::vm::Vm> as *const ()),
(
"monthCode",
oxide_builtins::temporal::plain_year_month_month_code::<crate::vm::Vm> as *const (),
),
(
"calendarId",
oxide_builtins::temporal::plain_year_month_calendar_id::<crate::vm::Vm> as *const (),
),
(
"daysInMonth",
oxide_builtins::temporal::plain_year_month_days_in_month::<crate::vm::Vm> as *const (),
),
(
"daysInYear",
oxide_builtins::temporal::plain_year_month_days_in_year::<crate::vm::Vm> as *const (),
),
(
"monthsInYear",
oxide_builtins::temporal::plain_year_month_months_in_year::<crate::vm::Vm> as *const (),
),
(
"inLeapYear",
oxide_builtins::temporal::plain_year_month_in_leap_year::<crate::vm::Vm> as *const (),
),
("era", oxide_builtins::temporal::plain_year_month_era::<crate::vm::Vm> as *const ()),
(
"eraYear",
oxide_builtins::temporal::plain_year_month_era_year::<crate::vm::Vm> as *const (),
),
] {
bind_accessor_getter(core, session, plain_year_month_proto, name, getter);
}
apply_binding_table(
world,
plain_year_month_proto,
core,
&[
(
"toString",
oxide_builtins::temporal::plain_year_month_to_string::<crate::vm::Vm> as *const (),
0,
),
(
"toJSON",
oxide_builtins::temporal::plain_year_month_to_json::<crate::vm::Vm> as *const (),
0,
),
],
);
bind_well_known_data_property(
core,
plain_year_month_proto,
9,
JsValue::perm_string(
core.perm_interner()
.string_ptr(core.perm_interner().intern("Temporal.PlainYearMonth").0),
),
PropAttributes::new(false, false, true),
);
// 把子对象挂到 Temporal 命名空间对象上,再把 Temporal 挂到 global。
bind_global_value(core, temporal, "Now", JsValue::from_js_object(now_ptr));
bind_global_value(core, temporal, "Instant", JsValue::from_js_object(instant_ctor_ptr));
@@ -965,5 +1109,7 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
bind_global_value(core, temporal, "PlainDateTime", JsValue::from_js_object(plain_date_time_ctor_ptr));
bind_global_value(core, temporal, "Duration", JsValue::from_js_object(duration_ctor_ptr));
bind_global_value(core, temporal, "ZonedDateTime", JsValue::from_js_object(zoned_date_time_ctor_ptr));
bind_global_value(core, temporal, "PlainMonthDay", JsValue::from_js_object(plain_month_day_ctor_ptr));
bind_global_value(core, temporal, "PlainYearMonth", JsValue::from_js_object(plain_year_month_ctor_ptr));
bind_global_value(core, global, "Temporal", JsValue::from_js_object(temporal_ptr));
}
@@ -2604,3 +2604,196 @@ fn duration_total_relative_zdt() {
.unwrap();
assert_eq!(r.as_double(), 1.0);
}
// -- Temporal.PlainMonthDay / PlainYearMonth(E 批)--
#[test]
fn plain_month_day_ctor_basic_and_instanceof() {
let mut vm = Vm::new();
// 构造成功:instanceof / length / name / 槽值 / 参考年默认 1972 经 always 形回读。
let r = eval(
&mut vm,
"(() => {
const md = new Temporal.PlainMonthDay(5, 2);
return [
md instanceof Temporal.PlainMonthDay,
Temporal.PlainMonthDay.length,
Temporal.PlainMonthDay.name,
Temporal.PlainYearMonth.length,
md.monthCode,
md.day,
md.calendarId,
md.toString({calendarName: 'always'}),
].join('|');
})()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "true|2|PlainMonthDay|2|M05|2|iso8601|1972-05-02[u-ca=iso8601]");
}
#[test]
fn plain_month_day_ctor_rejects() {
let mut vm = Vm::new();
// BigInt/Symbol 分量 → TypeError;NaN/±Inf → RangeError;缺参 → RangeError;
// 非闰参考年 2/29 → RangeError,闰参考年 1972 合法;非 new 调用 → TypeError。
let r = eval(
&mut vm,
"(() => {
const kind = (fn) => { try { fn(); return 'no'; } catch (e) { return e.constructor.name; } };
return [
kind(() => new Temporal.PlainMonthDay(1n, 1)),
kind(() => new Temporal.PlainMonthDay(Symbol(), 1)),
kind(() => new Temporal.PlainMonthDay(NaN, 1)),
kind(() => new Temporal.PlainMonthDay(1, Infinity)),
kind(() => new Temporal.PlainMonthDay()),
kind(() => new Temporal.PlainMonthDay(2, 29, 'iso8601', 2023)),
new Temporal.PlainMonthDay(2, 29).day,
kind(() => Temporal.PlainMonthDay(1, 2)),
].join('|');
})()",
)
.unwrap();
assert_eq!(
str_val(&vm, r),
"TypeError|TypeError|RangeError|RangeError|RangeError|RangeError|29|TypeError"
);
}
#[test]
fn plain_month_day_ctor_calendar_argument() {
let mut vm = Vm::new();
// 日历参数:{} → TypeError;函数对象按缺省 iso8601;大小写不敏感白名单;
// 未知字符串 → RangeError。
let r = eval(
&mut vm,
"(() => {
const kind = (fn) => { try { fn(); return 'no'; } catch (e) { return e.constructor.name; } };
return [
kind(() => new Temporal.PlainMonthDay(1, 1, {})),
new Temporal.PlainMonthDay(1, 1, () => 'iso8601').calendarId,
new Temporal.PlainMonthDay(1, 1, 'iSo8601').calendarId,
kind(() => new Temporal.PlainMonthDay(1, 1, 'local')),
].join('|');
})()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "TypeError|iso8601|iso8601|RangeError");
}
#[test]
fn plain_year_month_ctor_limits_and_ref_day() {
let mut vm = Vm::new();
// 年月边界:-271821-03 与 275760-10 越界抛 RangeError,相邻月合法;
// refISODay 缺省 1,须落在目标月长内;非 new 调用 → TypeError;
// NaN 分量(undefined/不可解析串)→ RangeError,null → 0。
let r = eval(
&mut vm,
"(() => {
const kind = (fn) => { try { fn(); return 'no'; } catch (e) { return e.constructor.name; } };
const ym = new Temporal.PlainYearMonth(-271821, 4, 'iso8601', 18);
return [
kind(() => new Temporal.PlainYearMonth(-271821, 3)),
kind(() => new Temporal.PlainYearMonth(275760, 10)),
new Temporal.PlainYearMonth(-271821, 4).monthCode,
new Temporal.PlainYearMonth(275760, 9).monthCode,
ym.toString({calendarName: 'always'}),
new Temporal.PlainYearMonth(2021, 2).toString({calendarName: 'always'}),
kind(() => new Temporal.PlainYearMonth(2021, 2, 'iso8601', 0)),
kind(() => new Temporal.PlainYearMonth(2021, 2, 'iso8601', 29)),
kind(() => new Temporal.PlainYearMonth(undefined, 11)),
kind(() => new Temporal.PlainYearMonth('invalid', 11)),
new Temporal.PlainYearMonth(null, 11).year,
kind(() => new Temporal.PlainMonthDay(undefined, 24)),
kind(() => Temporal.PlainYearMonth(1970, 1)),
].join('|');
})()",
)
.unwrap();
assert_eq!(
str_val(&vm, r),
"RangeError|RangeError|M04|M09|-271821-04-18[u-ca=iso8601]|2021-02-01[u-ca=iso8601]|RangeError|RangeError|RangeError|RangeError|0|RangeError|TypeError"
);
}
#[test]
fn plain_year_month_getters() {
let mut vm = Vm::new();
// 槽值 getter + 日历直读 + ISO 日历无纪元(era/eraYear 恒 undefined)。
let r = eval(
&mut vm,
"(() => {
const ym = new Temporal.PlainYearMonth(-1, 8);
return [
ym.year, ym.month, ym.monthCode, ym.calendarId,
ym.daysInMonth, ym.daysInYear, ym.monthsInYear, ym.inLeapYear,
String(ym.era), String(ym.eraYear),
].join('|');
})()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "-1|8|M08|iso8601|31|365|12|false|undefined|undefined");
}
#[test]
fn plain_month_day_to_string_and_to_json() {
let mut vm = Vm::new();
// 默认/auto/never 同形;critical 带 ! 注解;toJSON = 默认形且忽略参数;
// 非法 calendarName → RangeError;options 非对象 → TypeError。
let r = eval(
&mut vm,
"(() => {
const md = new Temporal.PlainMonthDay(5, 2);
const kind = (fn) => { try { fn(); return 'no'; } catch (e) { return e.constructor.name; } };
return [
md.toString(),
md.toString({calendarName: 'auto'}),
md.toString({calendarName: 'never'}),
md.toString({calendarName: 'critical'}),
md.toJSON(),
kind(() => md.toString({calendarName: 'ALWAYS'})),
kind(() => md.toString(1)),
md.toString({}),
].join('|');
})()",
)
.unwrap();
assert_eq!(
str_val(&vm, r),
"05-02|05-02|05-02|1972-05-02[!u-ca=iso8601]|05-02|RangeError|TypeError|05-02"
);
}
#[test]
fn plain_year_month_to_string_year_format() {
let mut vm = Vm::new();
// 年份格式化:-1 → -000001(6 位带符号);always 形补参考日。
let r = eval(
&mut vm,
"(() => {
const ym = new Temporal.PlainYearMonth(-1, 8);
return [ym.toString(), ym.toString({calendarName: 'always'}), ym.toJSON()].join('|');
})()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "-000001-08|-000001-08-01[u-ca=iso8601]|-000001-08");
}
#[test]
fn plain_month_day_and_year_month_branding() {
let mut vm = Vm::new();
// receiver 非实例 → TypeError(toString / getter 均校验品牌)。
let r = eval(
&mut vm,
"(() => {
const kind = (fn) => { try { fn(); return 'no'; } catch (e) { return e.constructor.name; } };
return [
kind(() => Temporal.PlainMonthDay.prototype.toString.call(1)),
kind(() => Temporal.PlainMonthDay.prototype.day.call({})),
kind(() => Temporal.PlainYearMonth.prototype.toString.call('x')),
kind(() => Temporal.PlainYearMonth.prototype.year.call(null)),
].join('|');
})()",
)
.unwrap();
assert_eq!(str_val(&vm, r), "TypeError|TypeError|TypeError|TypeError");
}