feat: implement Temporal Instant zoned conversion
This commit is contained in:
@@ -2,7 +2,8 @@ use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
|
|||||||
use oxide_kernel::string_forge::PermInterner;
|
use oxide_kernel::string_forge::PermInterner;
|
||||||
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
||||||
use oxide_types::private_key::{
|
use oxide_types::private_key::{
|
||||||
is_private_name_key, is_symbol_key, symbol_index_from_key, well_known_symbol_id_from_key,
|
is_private_name_key, is_symbol_key, make_well_known_symbol_key, symbol_index_from_key,
|
||||||
|
well_known_symbol_id_from_key,
|
||||||
};
|
};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
@@ -110,7 +111,8 @@ fn decode_symbol_key<H: VmHost>(vm: &H, key: u32) -> JsValue {
|
|||||||
5 => world.sym_to_primitive.as_ptr(),
|
5 => world.sym_to_primitive.as_ptr(),
|
||||||
6 => world.sym_has_instance.as_ptr(),
|
6 => world.sym_has_instance.as_ptr(),
|
||||||
7 => world.sym_match_all.as_ptr(),
|
7 => world.sym_match_all.as_ptr(),
|
||||||
_ => world.sym_async_iterator.as_ptr(),
|
8 => world.sym_async_iterator.as_ptr(),
|
||||||
|
_ => world.sym_to_string_tag.as_ptr(),
|
||||||
};
|
};
|
||||||
return JsValue::from_js_object(ptr as *mut JsObject);
|
return JsValue::from_js_object(ptr as *mut JsObject);
|
||||||
}
|
}
|
||||||
@@ -643,29 +645,13 @@ pub fn object_get_own_property_descriptor<H: VmHost>(vm: &mut H, args: &[u8]) ->
|
|||||||
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
|
Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)),
|
||||||
};
|
};
|
||||||
let obj_ptr = obj_val.as_js_object_ptr();
|
let obj_ptr = obj_val.as_js_object_ptr();
|
||||||
let prop_name_str = oxide_runtime_api::to_string(vm.reg(args[2]));
|
let key = vm.property_key_si(vm.reg(args[2]));
|
||||||
let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0;
|
let obj = unsafe { &*obj_ptr };
|
||||||
|
let Some(offset) = vm.get_own_property_slot(obj, key) else {
|
||||||
let (found_value, found_meta, found) = {
|
|
||||||
let obj = unsafe { &*obj_ptr };
|
|
||||||
let keys = walk_own_keys(vm, obj);
|
|
||||||
let mut found_value = JsValue::undefined();
|
|
||||||
let mut found_meta = None;
|
|
||||||
let mut found = false;
|
|
||||||
for (prop_si, offset) in keys {
|
|
||||||
if prop_si == si {
|
|
||||||
found_value = obj.get_prop_at(offset);
|
|
||||||
found_meta = obj.prop_meta_at(offset);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(found_value, found_meta, found)
|
|
||||||
};
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return NativeResult::Ok(JsValue::undefined());
|
return NativeResult::Ok(JsValue::undefined());
|
||||||
}
|
};
|
||||||
|
let found_value = obj.get_prop_at(offset);
|
||||||
|
let found_meta = obj.prop_meta_at(offset);
|
||||||
|
|
||||||
let sf_ptr = vm.kernel_core().perm_interner().as_ref() as *const PermInterner;
|
let sf_ptr = vm.kernel_core().perm_interner().as_ref() as *const PermInterner;
|
||||||
let sh_ptr = vm.kernel_core().shape_forge().as_ref() as *const ShapeForge;
|
let sh_ptr = vm.kernel_core().shape_forge().as_ref() as *const ShapeForge;
|
||||||
@@ -1029,8 +1015,16 @@ pub fn object_proto_to_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
|||||||
"Object"
|
"Object"
|
||||||
};
|
};
|
||||||
// @@toStringTag 为字符串时覆盖内置标签;getter 抛错透传原异常。
|
// @@toStringTag 为字符串时覆盖内置标签;getter 抛错透传原异常。
|
||||||
let tag_si = vm.kernel_core().perm_interner().intern("@@toStringTag").0;
|
let tag_key = make_well_known_symbol_key(9);
|
||||||
match vm.ordinary_get(obj, tag_si, this_val) {
|
let tag_value = match vm.ordinary_get(obj, tag_key, this_val) {
|
||||||
|
Ok(value) if !value.is_undefined() => Ok(value),
|
||||||
|
Ok(_) => {
|
||||||
|
let legacy_key = vm.kernel_core().perm_interner().intern("@@toStringTag").0;
|
||||||
|
vm.ordinary_get(obj, legacy_key, this_val)
|
||||||
|
}
|
||||||
|
Err(err) => Err(err),
|
||||||
|
};
|
||||||
|
match tag_value {
|
||||||
Ok(v) => match vm.lookup_str(v) {
|
Ok(v) => match vm.lookup_str(v) {
|
||||||
Some(s) => return NativeResult::Ok(vm.new_string(&format!("[object {s}]"))),
|
Some(s) => return NativeResult::Ok(vm.new_string(&format!("[object {s}]"))),
|
||||||
None => builtin,
|
None => builtin,
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ use oxide_types::object::JsObject;
|
|||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
// Temporal 命名空间的最小实现子集:Temporal.Now / Temporal.Instant /
|
// Temporal 命名空间的最小实现子集:Temporal.Now / Temporal.Instant /
|
||||||
// Temporal.PlainDate / Temporal.PlainTime。内部数据按对象类型存入 prop 槽:
|
// Temporal.PlainDate / Temporal.PlainTime / Temporal.ZonedDateTime。内部数据按对象类型存入 prop 槽:
|
||||||
// Instant 存纪元纳秒(BigInt,prop 0)、PlainDate 存年/月/日(prop 0-2)、
|
// Instant 存纪元纳秒(BigInt,prop 0)、PlainDate 存年/月/日(prop 0-2)、
|
||||||
// PlainTime 存午夜后纳秒(f64,prop 0)。
|
// PlainTime 存午夜后纳秒(f64,prop 0),ZonedDateTime 存纪元纳秒、时区 ID、日历 ID(prop 0-2)。
|
||||||
|
|
||||||
const MAX_INSTANT_NS: i128 = 8_640_000_000_000_000_000_000;
|
const MAX_INSTANT_NS: i128 = 8_640_000_000_000_000_000_000;
|
||||||
|
|
||||||
@@ -69,6 +69,13 @@ fn ensure_duration<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<(), JsValue>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_zoned_date_time<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<(), JsValue> {
|
||||||
|
if !obj.is_zoned_date_time_obj() {
|
||||||
|
return Err(crate::error::create_type_error(vm, "called on incompatible receiver"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn receiver_obj<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<*mut JsObject, JsValue> {
|
fn receiver_obj<H: VmHost>(vm: &mut H, args: &[u8]) -> Result<*mut JsObject, JsValue> {
|
||||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||||
if !raw.is_object() {
|
if !raw.is_object() {
|
||||||
@@ -127,6 +134,19 @@ fn make_instant<H: VmHost>(vm: &mut H, epoch_ns: i128) -> NativeResult {
|
|||||||
NativeResult::Ok(JsValue::from_js_object(vm.alloc_object(obj)))
|
NativeResult::Ok(JsValue::from_js_object(vm.alloc_object(obj)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_zoned_date_time<H: VmHost>(vm: &mut H, epoch_ns: i128, time_zone_id: &str) -> NativeResult {
|
||||||
|
let proto = JsValue::from_js_object(vm.session().builtin_world().zoned_date_time_proto.as_ptr() as *mut JsObject);
|
||||||
|
let epoch_value = vm.new_bigint(epoch_ns);
|
||||||
|
let time_zone_value = vm.new_string(time_zone_id);
|
||||||
|
let calendar_value = vm.new_string("iso8601");
|
||||||
|
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto);
|
||||||
|
obj.type_tag = JsObject::OBJ_TYPE_ZONED_DATE_TIME;
|
||||||
|
obj.set_prop_at(0, epoch_value);
|
||||||
|
obj.set_prop_at(1, time_zone_value);
|
||||||
|
obj.set_prop_at(2, calendar_value);
|
||||||
|
NativeResult::Ok(JsValue::from_js_object(vm.alloc_object(obj)))
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_digits(bytes: &[u8], cursor: &mut usize, count: usize) -> Option<i128> {
|
fn parse_digits(bytes: &[u8], cursor: &mut usize, count: usize) -> Option<i128> {
|
||||||
let end = cursor.checked_add(count)?;
|
let end = cursor.checked_add(count)?;
|
||||||
let digits = bytes.get(*cursor..end)?;
|
let digits = bytes.get(*cursor..end)?;
|
||||||
@@ -986,13 +1006,13 @@ fn parse_offset_minutes(value: &str) -> Option<i32> {
|
|||||||
Some(if bytes[0] == b'-' { -magnitude } else { magnitude })
|
Some(if bytes[0] == b'-' { -magnitude } else { magnitude })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instant_time_zone_offset(value: &str) -> Option<i32> {
|
fn canonical_time_zone(value: &str) -> Option<(String, i32)> {
|
||||||
let input = value.trim();
|
let input = value.trim();
|
||||||
if input.eq_ignore_ascii_case("UTC") || input.eq_ignore_ascii_case("Z") {
|
if input.eq_ignore_ascii_case("UTC") || input.eq_ignore_ascii_case("Z") {
|
||||||
return Some(0);
|
return Some(("UTC".to_string(), 0));
|
||||||
}
|
}
|
||||||
if let Some(offset) = parse_offset_minutes(input) {
|
if let Some(offset) = parse_offset_minutes(input) {
|
||||||
return Some(offset);
|
return Some((input.to_string(), offset));
|
||||||
}
|
}
|
||||||
if input.starts_with("-000000") {
|
if input.starts_with("-000000") {
|
||||||
return None;
|
return None;
|
||||||
@@ -1005,21 +1025,26 @@ fn instant_time_zone_offset(value: &str) -> Option<i32> {
|
|||||||
}
|
}
|
||||||
let annotation = &input[open + 1..input.len() - 1];
|
let annotation = &input[open + 1..input.len() - 1];
|
||||||
if annotation.eq_ignore_ascii_case("UTC") {
|
if annotation.eq_ignore_ascii_case("UTC") {
|
||||||
return Some(0);
|
return Some(("UTC".to_string(), 0));
|
||||||
}
|
}
|
||||||
return parse_offset_minutes(annotation);
|
return parse_offset_minutes(annotation).map(|offset| (annotation.to_string(), offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_start = input.find(['T', 't', ' '])?;
|
let time_start = input.find(['T', 't', ' '])?;
|
||||||
let time = &input[time_start + 1..];
|
let time = &input[time_start + 1..];
|
||||||
if time.ends_with(['Z', 'z']) {
|
if time.ends_with(['Z', 'z']) {
|
||||||
return Some(0);
|
return Some(("UTC".to_string(), 0));
|
||||||
}
|
}
|
||||||
let offset_start = time
|
let offset_start = time
|
||||||
.char_indices()
|
.char_indices()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(index, ch)| matches!(ch, '+' | '-').then_some(index))?;
|
.find_map(|(index, ch)| matches!(ch, '+' | '-').then_some(index))?;
|
||||||
parse_offset_minutes(&time[offset_start..])
|
let offset_id = &time[offset_start..];
|
||||||
|
parse_offset_minutes(offset_id).map(|offset| (offset_id.to_string(), offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instant_time_zone_offset(value: &str) -> Option<i32> {
|
||||||
|
canonical_time_zone(value).map(|(_, offset)| offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn civil_from_days(days: i128) -> (i128, i128, i128) {
|
fn civil_from_days(days: i128) -> (i128, i128, i128) {
|
||||||
@@ -1199,11 +1224,98 @@ pub fn instant_to_locale_string<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeRes
|
|||||||
instant_default_string(vm, args)
|
instant_default_string(vm, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Temporal.Instant.prototype.toZonedDateTimeISO(timeZone)`:以同一纪元纳秒创建 ISO 日历 ZonedDateTime。
|
||||||
|
pub fn instant_to_zoned_date_time_iso<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let ptr = native_try!(receiver_obj(vm, args));
|
||||||
|
let obj = unsafe { &*ptr };
|
||||||
|
native_try!(ensure_instant(vm, obj));
|
||||||
|
let Some(epoch_ns) = get_instant_epoch_ns(obj) else {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "invalid Instant"));
|
||||||
|
};
|
||||||
|
if args.len() < 2 {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "timeZone is required"));
|
||||||
|
}
|
||||||
|
let raw = vm.reg(args[1]);
|
||||||
|
if !raw.is_string() {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "invalid time zone"));
|
||||||
|
}
|
||||||
|
let input = to_string(raw);
|
||||||
|
let Some((time_zone_id, _)) = canonical_time_zone(&input) else {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "invalid time zone"));
|
||||||
|
};
|
||||||
|
make_zoned_date_time(vm, epoch_ns, &time_zone_id)
|
||||||
|
}
|
||||||
|
|
||||||
/// `Temporal.Instant.prototype.valueOf()`:Temporal 对象禁止转原始值。
|
/// `Temporal.Instant.prototype.valueOf()`:Temporal 对象禁止转原始值。
|
||||||
pub fn instant_value_of<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
pub fn instant_value_of<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||||
NativeResult::Err(crate::error::create_type_error(vm, "Temporal.Instant has no valueOf"))
|
NativeResult::Err(crate::error::create_type_error(vm, "Temporal.Instant has no valueOf"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Temporal.ZonedDateTime` 构造器:保存纪元纳秒、固定偏移或 UTC 时区以及 ISO 日历。
|
||||||
|
pub fn zoned_date_time_constructor<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let ctor_proto = vm.session().builtin_world().zoned_date_time_proto.as_ptr();
|
||||||
|
if !is_ctor_call(vm, args, ctor_proto) {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(
|
||||||
|
vm,
|
||||||
|
"Class constructor Temporal.ZonedDateTime cannot be invoked without 'new'",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let epoch_raw = if args.len() < 2 { JsValue::undefined() } else { vm.reg(args[1]) };
|
||||||
|
let epoch_ns = native_try!(primitive_to_bigint(vm, epoch_raw));
|
||||||
|
if epoch_ns.unsigned_abs() > MAX_INSTANT_NS as u128 {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "ZonedDateTime outside supported range"));
|
||||||
|
}
|
||||||
|
if args.len() < 3 || !vm.reg(args[2]).is_string() {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "invalid time zone"));
|
||||||
|
}
|
||||||
|
let time_zone_input = to_string(vm.reg(args[2]));
|
||||||
|
let Some((time_zone_id, _)) = canonical_time_zone(&time_zone_input) else {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "invalid time zone"));
|
||||||
|
};
|
||||||
|
if args.len() >= 4 && !vm.reg(args[3]).is_undefined() {
|
||||||
|
let calendar = vm.reg(args[3]);
|
||||||
|
if !calendar.is_string() {
|
||||||
|
return NativeResult::Err(crate::error::create_type_error(vm, "invalid calendar"));
|
||||||
|
}
|
||||||
|
if !to_string(calendar).eq_ignore_ascii_case("iso8601") {
|
||||||
|
return NativeResult::Err(crate::error::create_range_error(vm, "invalid calendar"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let epoch_value = vm.new_bigint(epoch_ns);
|
||||||
|
let time_zone_value = vm.new_string(&time_zone_id);
|
||||||
|
let calendar_value = vm.new_string("iso8601");
|
||||||
|
initialize_temporal_receiver(
|
||||||
|
vm,
|
||||||
|
args,
|
||||||
|
JsObject::OBJ_TYPE_ZONED_DATE_TIME,
|
||||||
|
[epoch_value, time_zone_value, calendar_value],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Temporal.ZonedDateTime.prototype.epochNanoseconds`:返回精确纪元纳秒。
|
||||||
|
pub fn zoned_date_time_epoch_nanoseconds<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let ptr = native_try!(receiver_obj(vm, args));
|
||||||
|
let obj = unsafe { &*ptr };
|
||||||
|
native_try!(ensure_zoned_date_time(vm, obj));
|
||||||
|
NativeResult::Ok(obj.get_prop_at(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Temporal.ZonedDateTime.prototype.timeZoneId`:返回规范化时区标识符。
|
||||||
|
pub fn zoned_date_time_time_zone_id<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||||
|
let ptr = native_try!(receiver_obj(vm, args));
|
||||||
|
let obj = unsafe { &*ptr };
|
||||||
|
native_try!(ensure_zoned_date_time(vm, obj));
|
||||||
|
NativeResult::Ok(obj.get_prop_at(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Temporal.ZonedDateTime.prototype.calendarId`:最小实现固定返回 ISO 8601 日历。
|
||||||
|
pub fn zoned_date_time_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_zoned_date_time(vm, obj));
|
||||||
|
NativeResult::Ok(obj.get_prop_at(2))
|
||||||
|
}
|
||||||
|
|
||||||
fn make_plain_date<H: VmHost>(vm: &mut H, year: i32, month: u32, day: u32) -> NativeResult {
|
fn make_plain_date<H: VmHost>(vm: &mut H, year: i32, month: u32, day: u32) -> NativeResult {
|
||||||
let proto = JsValue::from_js_object(vm.session().builtin_world().plain_date_proto.as_ptr() as *mut JsObject);
|
let proto = JsValue::from_js_object(vm.session().builtin_world().plain_date_proto.as_ptr() as *mut JsObject);
|
||||||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto);
|
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, proto);
|
||||||
|
|||||||
@@ -226,6 +226,30 @@ fn test_get_own_property_descriptor_no_args_type_error() {
|
|||||||
assert!(eval("Object.getOwnPropertyDescriptor()").is_err());
|
assert!(eval("Object.getOwnPropertyDescriptor()").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_own_property_descriptor_supports_symbol_keys() {
|
||||||
|
let (_vm, result) = eval(
|
||||||
|
"var key = Symbol('key');
|
||||||
|
var object = {};
|
||||||
|
Object.defineProperty(object, key, { value: 7, writable: false });
|
||||||
|
var descriptor = Object.getOwnPropertyDescriptor(object, key);
|
||||||
|
descriptor.value === 7 && descriptor.writable === false",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.is_bool() && result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_own_property_descriptor_keeps_undefined_array_properties() {
|
||||||
|
let (_vm, result) = eval(
|
||||||
|
"var array = [];
|
||||||
|
Object.defineProperty(array, 'named', { value: undefined });
|
||||||
|
Object.getOwnPropertyDescriptor(array, 'named').value === undefined",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(result.is_bool() && result.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_has_own_property_boxes_primitive_this() {
|
fn test_has_own_property_boxes_primitive_this() {
|
||||||
// ToObject 装箱 this:原始值上 hasOwnProperty 返回 false 而非抛错。
|
// ToObject 装箱 this:原始值上 hasOwnProperty 返回 false 而非抛错。
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ pub struct BuiltinWorld {
|
|||||||
pub sym_has_instance: P<JsObject>,
|
pub sym_has_instance: P<JsObject>,
|
||||||
pub sym_match_all: P<JsObject>,
|
pub sym_match_all: P<JsObject>,
|
||||||
pub sym_async_iterator: P<JsObject>,
|
pub sym_async_iterator: P<JsObject>,
|
||||||
|
pub sym_to_string_tag: P<JsObject>,
|
||||||
pub temporal_object: P<JsObject>,
|
pub temporal_object: P<JsObject>,
|
||||||
pub temporal_now_object: P<JsObject>,
|
pub temporal_now_object: P<JsObject>,
|
||||||
pub instant_constructor: P<JsObject>,
|
pub instant_constructor: P<JsObject>,
|
||||||
@@ -253,6 +254,8 @@ pub struct BuiltinWorld {
|
|||||||
pub plain_time_proto: P<JsObject>,
|
pub plain_time_proto: P<JsObject>,
|
||||||
pub duration_constructor: P<JsObject>,
|
pub duration_constructor: P<JsObject>,
|
||||||
pub duration_proto: P<JsObject>,
|
pub duration_proto: P<JsObject>,
|
||||||
|
pub zoned_date_time_constructor: P<JsObject>,
|
||||||
|
pub zoned_date_time_proto: P<JsObject>,
|
||||||
pub bigint_constructor: P<JsObject>,
|
pub bigint_constructor: P<JsObject>,
|
||||||
pub bigint_proto: P<JsObject>,
|
pub bigint_proto: P<JsObject>,
|
||||||
pub stub_objects: Vec<P<JsObject>>,
|
pub stub_objects: Vec<P<JsObject>>,
|
||||||
@@ -507,10 +510,11 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
|
|||||||
wire_ctor_proto(&world.plain_date_constructor, &world.plain_date_proto);
|
wire_ctor_proto(&world.plain_date_constructor, &world.plain_date_proto);
|
||||||
wire_ctor_proto(&world.plain_time_constructor, &world.plain_time_proto);
|
wire_ctor_proto(&world.plain_time_constructor, &world.plain_time_proto);
|
||||||
wire_ctor_proto(&world.duration_constructor, &world.duration_proto);
|
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.bigint_constructor, &world.bigint_proto);
|
wire_ctor_proto(&world.bigint_constructor, &world.bigint_proto);
|
||||||
|
|
||||||
let obj_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
|
let obj_proto_val = JsValue::from_js_object(world.object_proto.as_ptr() as *mut JsObject);
|
||||||
let non_object_protos: [&P<JsObject>; 19] = [
|
let non_object_protos: [&P<JsObject>; 20] = [
|
||||||
&world.array_proto,
|
&world.array_proto,
|
||||||
&world.function_proto,
|
&world.function_proto,
|
||||||
&world.string_proto,
|
&world.string_proto,
|
||||||
@@ -529,6 +533,7 @@ fn wire_builtin_world_links(world: &BuiltinWorld) {
|
|||||||
&world.plain_date_proto,
|
&world.plain_date_proto,
|
||||||
&world.plain_time_proto,
|
&world.plain_time_proto,
|
||||||
&world.duration_proto,
|
&world.duration_proto,
|
||||||
|
&world.zoned_date_time_proto,
|
||||||
&world.bigint_proto,
|
&world.bigint_proto,
|
||||||
];
|
];
|
||||||
for proto in &non_object_protos {
|
for proto in &non_object_protos {
|
||||||
@@ -656,6 +661,7 @@ impl BuiltinWorld {
|
|||||||
BuiltinId::SymHasInstance => &self.sym_has_instance,
|
BuiltinId::SymHasInstance => &self.sym_has_instance,
|
||||||
BuiltinId::SymMatchAll => &self.sym_match_all,
|
BuiltinId::SymMatchAll => &self.sym_match_all,
|
||||||
BuiltinId::SymAsyncIterator => &self.sym_async_iterator,
|
BuiltinId::SymAsyncIterator => &self.sym_async_iterator,
|
||||||
|
BuiltinId::SymToStringTag => &self.sym_to_string_tag,
|
||||||
BuiltinId::TemporalObject => &self.temporal_object,
|
BuiltinId::TemporalObject => &self.temporal_object,
|
||||||
BuiltinId::TemporalNowObject => &self.temporal_now_object,
|
BuiltinId::TemporalNowObject => &self.temporal_now_object,
|
||||||
BuiltinId::InstantConstructor => &self.instant_constructor,
|
BuiltinId::InstantConstructor => &self.instant_constructor,
|
||||||
@@ -666,6 +672,8 @@ impl BuiltinWorld {
|
|||||||
BuiltinId::PlainTimeProto => &self.plain_time_proto,
|
BuiltinId::PlainTimeProto => &self.plain_time_proto,
|
||||||
BuiltinId::DurationConstructor => &self.duration_constructor,
|
BuiltinId::DurationConstructor => &self.duration_constructor,
|
||||||
BuiltinId::DurationProto => &self.duration_proto,
|
BuiltinId::DurationProto => &self.duration_proto,
|
||||||
|
BuiltinId::ZonedDateTimeConstructor => &self.zoned_date_time_constructor,
|
||||||
|
BuiltinId::ZonedDateTimeProto => &self.zoned_date_time_proto,
|
||||||
BuiltinId::BigIntConstructor => &self.bigint_constructor,
|
BuiltinId::BigIntConstructor => &self.bigint_constructor,
|
||||||
BuiltinId::BigIntProto => &self.bigint_proto,
|
BuiltinId::BigIntProto => &self.bigint_proto,
|
||||||
}
|
}
|
||||||
@@ -709,6 +717,7 @@ impl BuiltinWorld {
|
|||||||
let sym_has_instance = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
let sym_has_instance = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
let sym_match_all = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
let sym_match_all = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
let sym_async_iterator = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
let sym_async_iterator = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
|
let sym_to_string_tag = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
let temporal_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
let temporal_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
let temporal_now_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
let temporal_now_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||||
let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant");
|
let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant");
|
||||||
@@ -717,6 +726,8 @@ impl BuiltinWorld {
|
|||||||
let (plain_time_proto, plain_time_constructor) =
|
let (plain_time_proto, plain_time_constructor) =
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
||||||
let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration");
|
let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration");
|
||||||
|
let (zoned_date_time_proto, zoned_date_time_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime");
|
||||||
let (bigint_proto, bigint_constructor) = make_named_pair(string_forge, shape_forge, labels, "BigInt");
|
let (bigint_proto, bigint_constructor) = make_named_pair(string_forge, shape_forge, labels, "BigInt");
|
||||||
let stub_objects = Vec::new();
|
let stub_objects = Vec::new();
|
||||||
|
|
||||||
@@ -790,6 +801,7 @@ impl BuiltinWorld {
|
|||||||
sym_has_instance,
|
sym_has_instance,
|
||||||
sym_match_all,
|
sym_match_all,
|
||||||
sym_async_iterator,
|
sym_async_iterator,
|
||||||
|
sym_to_string_tag,
|
||||||
temporal_object,
|
temporal_object,
|
||||||
temporal_now_object,
|
temporal_now_object,
|
||||||
instant_constructor,
|
instant_constructor,
|
||||||
@@ -800,6 +812,8 @@ impl BuiltinWorld {
|
|||||||
plain_time_proto,
|
plain_time_proto,
|
||||||
duration_constructor,
|
duration_constructor,
|
||||||
duration_proto,
|
duration_proto,
|
||||||
|
zoned_date_time_constructor,
|
||||||
|
zoned_date_time_proto,
|
||||||
bigint_constructor,
|
bigint_constructor,
|
||||||
bigint_proto,
|
bigint_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
@@ -875,6 +889,7 @@ impl BuiltinWorld {
|
|||||||
sym_has_instance,
|
sym_has_instance,
|
||||||
sym_match_all,
|
sym_match_all,
|
||||||
sym_async_iterator,
|
sym_async_iterator,
|
||||||
|
sym_to_string_tag,
|
||||||
) = if dirty.symbol_family {
|
) = if dirty.symbol_family {
|
||||||
let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol");
|
let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol");
|
||||||
(
|
(
|
||||||
@@ -889,6 +904,7 @@ impl BuiltinWorld {
|
|||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
@@ -903,6 +919,7 @@ impl BuiltinWorld {
|
|||||||
current.sym_has_instance.clone(),
|
current.sym_has_instance.clone(),
|
||||||
current.sym_match_all.clone(),
|
current.sym_match_all.clone(),
|
||||||
current.sym_async_iterator.clone(),
|
current.sym_async_iterator.clone(),
|
||||||
|
current.sym_to_string_tag.clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -987,6 +1004,8 @@ impl BuiltinWorld {
|
|||||||
plain_time_constructor,
|
plain_time_constructor,
|
||||||
duration_proto,
|
duration_proto,
|
||||||
duration_constructor,
|
duration_constructor,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
zoned_date_time_constructor,
|
||||||
) = if dirty.temporal {
|
) = if dirty.temporal {
|
||||||
let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant");
|
let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant");
|
||||||
let (plain_date_proto, plain_date_constructor) =
|
let (plain_date_proto, plain_date_constructor) =
|
||||||
@@ -994,6 +1013,8 @@ impl BuiltinWorld {
|
|||||||
let (plain_time_proto, plain_time_constructor) =
|
let (plain_time_proto, plain_time_constructor) =
|
||||||
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
make_named_pair(string_forge, shape_forge, labels, "PlainTime");
|
||||||
let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration");
|
let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration");
|
||||||
|
let (zoned_date_time_proto, zoned_date_time_constructor) =
|
||||||
|
make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime");
|
||||||
(
|
(
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())),
|
||||||
@@ -1005,6 +1026,8 @@ impl BuiltinWorld {
|
|||||||
plain_time_constructor,
|
plain_time_constructor,
|
||||||
duration_proto,
|
duration_proto,
|
||||||
duration_constructor,
|
duration_constructor,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
zoned_date_time_constructor,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
@@ -1018,6 +1041,8 @@ impl BuiltinWorld {
|
|||||||
current.plain_time_constructor.clone(),
|
current.plain_time_constructor.clone(),
|
||||||
current.duration_proto.clone(),
|
current.duration_proto.clone(),
|
||||||
current.duration_constructor.clone(),
|
current.duration_constructor.clone(),
|
||||||
|
current.zoned_date_time_proto.clone(),
|
||||||
|
current.zoned_date_time_constructor.clone(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let (bigint_proto, bigint_constructor) = if dirty.stubs {
|
let (bigint_proto, bigint_constructor) = if dirty.stubs {
|
||||||
@@ -1097,6 +1122,7 @@ impl BuiltinWorld {
|
|||||||
sym_has_instance,
|
sym_has_instance,
|
||||||
sym_match_all,
|
sym_match_all,
|
||||||
sym_async_iterator,
|
sym_async_iterator,
|
||||||
|
sym_to_string_tag,
|
||||||
temporal_object,
|
temporal_object,
|
||||||
temporal_now_object,
|
temporal_now_object,
|
||||||
instant_constructor,
|
instant_constructor,
|
||||||
@@ -1107,6 +1133,8 @@ impl BuiltinWorld {
|
|||||||
plain_time_proto,
|
plain_time_proto,
|
||||||
duration_constructor,
|
duration_constructor,
|
||||||
duration_proto,
|
duration_proto,
|
||||||
|
zoned_date_time_constructor,
|
||||||
|
zoned_date_time_proto,
|
||||||
bigint_constructor,
|
bigint_constructor,
|
||||||
bigint_proto,
|
bigint_proto,
|
||||||
stub_objects,
|
stub_objects,
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ pub struct KernelSession {
|
|||||||
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
|
/// 维护注意:每个新增的 `BuiltinWorld` 对象字段都必须加到这里以及
|
||||||
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
|
/// `KernelSession::dirty_since_snapshot()`,以便选择性重置重建正确的
|
||||||
/// builtin 家族。
|
/// builtin 家族。
|
||||||
pub const NUM_BUILTINS: usize = 80;
|
pub const NUM_BUILTINS: usize = 83;
|
||||||
|
|
||||||
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
|
/// 内置对象枚举 id,与 `BuiltinWorld` 中的存储槽一一对应。
|
||||||
///
|
///
|
||||||
@@ -314,6 +314,9 @@ pub enum BuiltinId {
|
|||||||
BigIntProto = 77,
|
BigIntProto = 77,
|
||||||
DurationConstructor = 78,
|
DurationConstructor = 78,
|
||||||
DurationProto = 79,
|
DurationProto = 79,
|
||||||
|
SymToStringTag = 80,
|
||||||
|
ZonedDateTimeConstructor = 81,
|
||||||
|
ZonedDateTimeProto = 82,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuiltinId {
|
impl BuiltinId {
|
||||||
@@ -399,6 +402,9 @@ impl BuiltinId {
|
|||||||
BuiltinId::BigIntProto,
|
BuiltinId::BigIntProto,
|
||||||
BuiltinId::DurationConstructor,
|
BuiltinId::DurationConstructor,
|
||||||
BuiltinId::DurationProto,
|
BuiltinId::DurationProto,
|
||||||
|
BuiltinId::SymToStringTag,
|
||||||
|
BuiltinId::ZonedDateTimeConstructor,
|
||||||
|
BuiltinId::ZonedDateTimeProto,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +592,8 @@ impl KernelSession {
|
|||||||
|| gen(BuiltinId::SymToPrimitive) != snap(BuiltinId::SymToPrimitive)
|
|| gen(BuiltinId::SymToPrimitive) != snap(BuiltinId::SymToPrimitive)
|
||||||
|| gen(BuiltinId::SymHasInstance) != snap(BuiltinId::SymHasInstance)
|
|| gen(BuiltinId::SymHasInstance) != snap(BuiltinId::SymHasInstance)
|
||||||
|| gen(BuiltinId::SymMatchAll) != snap(BuiltinId::SymMatchAll)
|
|| gen(BuiltinId::SymMatchAll) != snap(BuiltinId::SymMatchAll)
|
||||||
|| gen(BuiltinId::SymAsyncIterator) != snap(BuiltinId::SymAsyncIterator),
|
|| gen(BuiltinId::SymAsyncIterator) != snap(BuiltinId::SymAsyncIterator)
|
||||||
|
|| gen(BuiltinId::SymToStringTag) != snap(BuiltinId::SymToStringTag),
|
||||||
math: gen(BuiltinId::MathObject) != snap(BuiltinId::MathObject),
|
math: gen(BuiltinId::MathObject) != snap(BuiltinId::MathObject),
|
||||||
json: gen(BuiltinId::JsonObject) != snap(BuiltinId::JsonObject),
|
json: gen(BuiltinId::JsonObject) != snap(BuiltinId::JsonObject),
|
||||||
date: gen(BuiltinId::DateConstructor) != snap(BuiltinId::DateConstructor)
|
date: gen(BuiltinId::DateConstructor) != snap(BuiltinId::DateConstructor)
|
||||||
@@ -633,7 +640,9 @@ impl KernelSession {
|
|||||||
|| gen(BuiltinId::PlainTimeConstructor) != snap(BuiltinId::PlainTimeConstructor)
|
|| gen(BuiltinId::PlainTimeConstructor) != snap(BuiltinId::PlainTimeConstructor)
|
||||||
|| gen(BuiltinId::PlainTimeProto) != snap(BuiltinId::PlainTimeProto)
|
|| gen(BuiltinId::PlainTimeProto) != snap(BuiltinId::PlainTimeProto)
|
||||||
|| gen(BuiltinId::DurationConstructor) != snap(BuiltinId::DurationConstructor)
|
|| gen(BuiltinId::DurationConstructor) != snap(BuiltinId::DurationConstructor)
|
||||||
|| gen(BuiltinId::DurationProto) != snap(BuiltinId::DurationProto),
|
|| gen(BuiltinId::DurationProto) != snap(BuiltinId::DurationProto)
|
||||||
|
|| gen(BuiltinId::ZonedDateTimeConstructor) != snap(BuiltinId::ZonedDateTimeConstructor)
|
||||||
|
|| gen(BuiltinId::ZonedDateTimeProto) != snap(BuiltinId::ZonedDateTimeProto),
|
||||||
stubs: world.stub_objects.len() != snapshot.stub_objects_len
|
stubs: world.stub_objects.len() != snapshot.stub_objects_len
|
||||||
|| stub_generations_dirty
|
|| stub_generations_dirty
|
||||||
|| gen(BuiltinId::BigIntConstructor) != snap(BuiltinId::BigIntConstructor)
|
|| gen(BuiltinId::BigIntConstructor) != snap(BuiltinId::BigIntConstructor)
|
||||||
|
|||||||
@@ -445,6 +445,8 @@ impl JsObject {
|
|||||||
pub const OBJ_TYPE_PLAIN_TIME: u8 = 16;
|
pub const OBJ_TYPE_PLAIN_TIME: u8 = 16;
|
||||||
/// Temporal.Duration 对象:十个时长分量依次存于 prop 0-9。
|
/// Temporal.Duration 对象:十个时长分量依次存于 prop 0-9。
|
||||||
pub const OBJ_TYPE_DURATION: u8 = 17;
|
pub const OBJ_TYPE_DURATION: u8 = 17;
|
||||||
|
/// Temporal.ZonedDateTime 对象:纪元纳秒、时区 ID、日历 ID 存于 prop 0-2。
|
||||||
|
pub const OBJ_TYPE_ZONED_DATE_TIME: u8 = 18;
|
||||||
/// `is_session_epoch` 字段中的 session 标记位。
|
/// `is_session_epoch` 字段中的 session 标记位。
|
||||||
pub const SESSION_EPOCH_BIT: u8 = 0x01;
|
pub const SESSION_EPOCH_BIT: u8 = 0x01;
|
||||||
/// `is_session_epoch` 字段中的 GC 标记位。
|
/// `is_session_epoch` 字段中的 GC 标记位。
|
||||||
@@ -530,6 +532,11 @@ impl JsObject {
|
|||||||
pub fn is_duration_obj(&self) -> bool {
|
pub fn is_duration_obj(&self) -> bool {
|
||||||
self.type_tag == Self::OBJ_TYPE_DURATION
|
self.type_tag == Self::OBJ_TYPE_DURATION
|
||||||
}
|
}
|
||||||
|
/// 是否 Temporal.ZonedDateTime 对象。
|
||||||
|
#[inline]
|
||||||
|
pub fn is_zoned_date_time_obj(&self) -> bool {
|
||||||
|
self.type_tag == Self::OBJ_TYPE_ZONED_DATE_TIME
|
||||||
|
}
|
||||||
|
|
||||||
/// 构造无属性、可扩展的空对象(`new Object()` 的基础对象)。
|
/// 构造无属性、可扩展的空对象(`new Object()` 的基础对象)。
|
||||||
pub fn new_empty(shape_id: ShapeId, proto: JsValue) -> Self {
|
pub fn new_empty(shape_id: ShapeId, proto: JsValue) -> Self {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub const PRIVATE_NAME_BASE: u32 = 0x8000_0000;
|
|||||||
pub const SYMBOL_KEY_BASE: u32 = 0xA000_0000;
|
pub const SYMBOL_KEY_BASE: u32 = 0xA000_0000;
|
||||||
|
|
||||||
/// 保留给 well-known symbol 的键槽位数(位于 Symbol 键区间最低端)。
|
/// 保留给 well-known symbol 的键槽位数(位于 Symbol 键区间最低端)。
|
||||||
pub const WELL_KNOWN_SYMBOL_COUNT: u32 = 9;
|
pub const WELL_KNOWN_SYMBOL_COUNT: u32 = 10;
|
||||||
|
|
||||||
/// 用户 symbol 下标的保留位掩码(低 28 位)。
|
/// 用户 symbol 下标的保留位掩码(低 28 位)。
|
||||||
const SYMBOL_INDEX_MASK: u32 = 0x0FFF_FFFF;
|
const SYMBOL_INDEX_MASK: u32 = 0x0FFF_FFFF;
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
|||||||
"asyncIterator",
|
"asyncIterator",
|
||||||
JsValue::from_js_object(session.builtin_world().sym_async_iterator.as_ptr() as *mut JsObject),
|
JsValue::from_js_object(session.builtin_world().sym_async_iterator.as_ptr() as *mut JsObject),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"toStringTag",
|
||||||
|
JsValue::from_js_object(session.builtin_world().sym_to_string_tag.as_ptr() as *mut JsObject),
|
||||||
|
),
|
||||||
] {
|
] {
|
||||||
bind_well_known_symbol(core, ctor, name, val);
|
bind_well_known_symbol(core, ctor, name, val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::bindings::{apply_binding_table, bind_accessor_getter, bind_global_value, configure_native_constructor};
|
use crate::bindings::{
|
||||||
|
apply_binding_table, bind_accessor_getter, bind_global_value, bind_well_known_data_property,
|
||||||
|
configure_native_constructor,
|
||||||
|
};
|
||||||
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
use oxide_kernel::kernel::{KernelCore, KernelSession};
|
||||||
use oxide_types::object::JsObject;
|
use oxide_types::object::{JsObject, PropAttributes};
|
||||||
use oxide_types::value::JsValue;
|
use oxide_types::value::JsValue;
|
||||||
|
|
||||||
/// 把 Temporal 命名空间及其子对象绑定到 global。
|
/// 把 Temporal 命名空间及其子对象绑定到 global。
|
||||||
@@ -98,10 +101,67 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
|||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
("toString", oxide_builtins::temporal::instant_to_string::<crate::vm::Vm> as *const (), 0),
|
("toString", oxide_builtins::temporal::instant_to_string::<crate::vm::Vm> as *const (), 0),
|
||||||
|
(
|
||||||
|
"toZonedDateTimeISO",
|
||||||
|
oxide_builtins::temporal::instant_to_zoned_date_time_iso::<crate::vm::Vm> as *const (),
|
||||||
|
1,
|
||||||
|
),
|
||||||
("until", oxide_builtins::temporal::instant_until::<crate::vm::Vm> as *const (), 1),
|
("until", oxide_builtins::temporal::instant_until::<crate::vm::Vm> as *const (), 1),
|
||||||
("valueOf", oxide_builtins::temporal::instant_value_of::<crate::vm::Vm> as *const (), 0),
|
("valueOf", oxide_builtins::temporal::instant_value_of::<crate::vm::Vm> as *const (), 0),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
bind_well_known_data_property(
|
||||||
|
core,
|
||||||
|
instant_proto,
|
||||||
|
9,
|
||||||
|
JsValue::perm_string(
|
||||||
|
core.perm_interner()
|
||||||
|
.string_ptr(core.perm_interner().intern("Temporal.Instant").0),
|
||||||
|
),
|
||||||
|
PropAttributes::new(false, false, true),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Temporal.ZonedDateTime:最小构造器与三个稳定内部槽 getter。
|
||||||
|
let zoned_date_time_ctor_ptr = world.zoned_date_time_constructor.as_ptr() as *mut JsObject;
|
||||||
|
let zoned_date_time_ctor = unsafe { &mut *zoned_date_time_ctor_ptr };
|
||||||
|
configure_native_constructor(
|
||||||
|
zoned_date_time_ctor,
|
||||||
|
oxide_builtins::temporal::zoned_date_time_constructor::<crate::vm::Vm> as *const (),
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
let zoned_date_time_proto_ptr = world.zoned_date_time_proto.as_ptr() as *mut JsObject;
|
||||||
|
let zoned_date_time_proto = unsafe { &mut *zoned_date_time_proto_ptr };
|
||||||
|
bind_accessor_getter(
|
||||||
|
core,
|
||||||
|
session,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
"epochNanoseconds",
|
||||||
|
oxide_builtins::temporal::zoned_date_time_epoch_nanoseconds::<crate::vm::Vm> as *const (),
|
||||||
|
);
|
||||||
|
bind_accessor_getter(
|
||||||
|
core,
|
||||||
|
session,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
"timeZoneId",
|
||||||
|
oxide_builtins::temporal::zoned_date_time_time_zone_id::<crate::vm::Vm> as *const (),
|
||||||
|
);
|
||||||
|
bind_accessor_getter(
|
||||||
|
core,
|
||||||
|
session,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
"calendarId",
|
||||||
|
oxide_builtins::temporal::zoned_date_time_calendar_id::<crate::vm::Vm> as *const (),
|
||||||
|
);
|
||||||
|
bind_well_known_data_property(
|
||||||
|
core,
|
||||||
|
zoned_date_time_proto,
|
||||||
|
9,
|
||||||
|
JsValue::perm_string(
|
||||||
|
core.perm_interner()
|
||||||
|
.string_ptr(core.perm_interner().intern("Temporal.ZonedDateTime").0),
|
||||||
|
),
|
||||||
|
PropAttributes::new(false, false, true),
|
||||||
|
);
|
||||||
|
|
||||||
// Temporal.PlainDate:构造器 + from 静态方法 + year/month/day getter 与 toString。
|
// Temporal.PlainDate:构造器 + from 静态方法 + year/month/day getter 与 toString。
|
||||||
let plain_date_ctor_ptr = world.plain_date_constructor.as_ptr() as *mut JsObject;
|
let plain_date_ctor_ptr = world.plain_date_constructor.as_ptr() as *mut JsObject;
|
||||||
@@ -376,5 +436,6 @@ pub fn bind_temporal(core: &Arc<KernelCore>, session: &KernelSession, global: &m
|
|||||||
bind_global_value(core, temporal, "PlainDate", JsValue::from_js_object(plain_date_ctor_ptr));
|
bind_global_value(core, temporal, "PlainDate", JsValue::from_js_object(plain_date_ctor_ptr));
|
||||||
bind_global_value(core, temporal, "PlainTime", JsValue::from_js_object(plain_time_ctor_ptr));
|
bind_global_value(core, temporal, "PlainTime", JsValue::from_js_object(plain_time_ctor_ptr));
|
||||||
bind_global_value(core, temporal, "Duration", JsValue::from_js_object(duration_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, global, "Temporal", JsValue::from_js_object(temporal_ptr));
|
bind_global_value(core, global, "Temporal", JsValue::from_js_object(temporal_ptr));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,6 +222,18 @@ pub(crate) fn bind_well_known_method_alias(
|
|||||||
proto.bump_generation();
|
proto.bump_generation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 在对象上按 well-known symbol 键绑定数据属性。
|
||||||
|
pub(crate) fn bind_well_known_data_property(
|
||||||
|
core: &Arc<KernelCore>, target: &mut JsObject, well_known_id: u32, value: JsValue, attributes: PropAttributes,
|
||||||
|
) {
|
||||||
|
let key = oxide_types::private_key::make_well_known_symbol_key(well_known_id);
|
||||||
|
let new_shape = core.shape_forge().make_shape(target.shape_id(), key);
|
||||||
|
target.set_shape_id(new_shape);
|
||||||
|
let pos = target.push_prop(value);
|
||||||
|
target.set_data_meta(pos, attributes);
|
||||||
|
target.bump_generation();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn bind_global_value(core: &Arc<KernelCore>, global: &mut JsObject, name: &str, value: JsValue) {
|
pub(crate) fn bind_global_value(core: &Arc<KernelCore>, global: &mut JsObject, name: &str, value: JsValue) {
|
||||||
let si = core.perm_interner().intern(name).0;
|
let si = core.perm_interner().intern(name).0;
|
||||||
let shape = core.shape_forge().make_shape(global.shape_id(), si);
|
let shape = core.shape_forge().make_shape(global.shape_id(), si);
|
||||||
|
|||||||
@@ -905,6 +905,8 @@ impl Vm {
|
|||||||
Some(7)
|
Some(7)
|
||||||
} else if std::ptr::eq(ptr, world.sym_async_iterator.as_ptr()) {
|
} else if std::ptr::eq(ptr, world.sym_async_iterator.as_ptr()) {
|
||||||
Some(8)
|
Some(8)
|
||||||
|
} else if std::ptr::eq(ptr, world.sym_to_string_tag.as_ptr()) {
|
||||||
|
Some(9)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -985,7 +987,7 @@ impl Vm {
|
|||||||
// 数组属性存储索引 = array_prop_count + shape 槽位(与元素区分)。
|
// 数组属性存储索引 = array_prop_count + shape 槽位(与元素区分)。
|
||||||
let idx = obj.array_prop_count as usize + pos as usize;
|
let idx = obj.array_prop_count as usize + pos as usize;
|
||||||
let val = obj.get_prop_at(idx);
|
let val = obj.get_prop_at(idx);
|
||||||
if !val.is_undefined() {
|
if !val.is_undefined() || obj.prop_vec_len() > idx {
|
||||||
Some(idx as u32)
|
Some(idx as u32)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -323,6 +323,43 @@ fn instant_json_and_locale_string_use_default_iso_output() {
|
|||||||
assert!(r.as_bool());
|
assert!(r.as_bool());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instant_to_string_tag_has_temporal_descriptor() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let r = eval(
|
||||||
|
&mut vm,
|
||||||
|
"let descriptor = Object.getOwnPropertyDescriptor(Temporal.Instant.prototype, Symbol.toStringTag);
|
||||||
|
Object.prototype.toString.call(Temporal.Instant.prototype) === '[object Temporal.Instant]'
|
||||||
|
&& descriptor.value === 'Temporal.Instant'
|
||||||
|
&& descriptor.writable === false
|
||||||
|
&& descriptor.enumerable === false
|
||||||
|
&& descriptor.configurable === true",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(r.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instant_to_zoned_date_time_iso_preserves_epoch_and_time_zone() {
|
||||||
|
let mut vm = Vm::new();
|
||||||
|
let r = eval(
|
||||||
|
&mut vm,
|
||||||
|
"let instant = new Temporal.Instant(1000000000000000000n);
|
||||||
|
let utc = instant.toZonedDateTimeISO('uTc');
|
||||||
|
let offset = instant.toZonedDateTimeISO('2021-08-19T17:30-07:00');
|
||||||
|
let constructed = new Temporal.ZonedDateTime(0n, '+01:30');
|
||||||
|
utc.epochNanoseconds === instant.epochNanoseconds
|
||||||
|
&& utc.timeZoneId === 'UTC'
|
||||||
|
&& utc.calendarId === 'iso8601'
|
||||||
|
&& offset.timeZoneId === '-07:00'
|
||||||
|
&& constructed.epochNanoseconds === 0n
|
||||||
|
&& constructed.timeZoneId === '+01:30'
|
||||||
|
&& Object.prototype.toString.call(constructed) === '[object Temporal.ZonedDateTime]'",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(r.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn instant_epoch_factories_validate_input_and_range() {
|
fn instant_epoch_factories_validate_input_and_range() {
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user