feat: 支持 Date/Map/Set 成员方法

This commit is contained in:
爱特每羊队
2026-06-08 03:25:07 +08:00
parent 4ed8379ed9
commit 55b544da82
15 changed files with 2073 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::native::NativeResult;
use crate::vm::Vm;
pub fn boolean_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let bool_val = if args.len() > 1 {
let arg = vm.reg(args[1]);
if arg.is_undefined() || arg.is_null() {
false
} else if arg.is_bool() {
arg.as_bool()
} else if arg.is_string() {
let s = vm
.kernel()
.string_forge()
.lookup(arg.as_string_index())
.unwrap_or_default();
!s.is_empty()
} else if arg.is_object() {
true
} else if arg.is_int() {
arg.as_int() != 0
} else if arg.is_double() {
arg.as_double() != 0.0
} else {
false
}
} else {
false
};
let boolean_proto = vm.kernel().builtin_world().boolean_proto.as_ptr() as *mut JsObject;
let is_ctor = if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let proto_ptr = unsafe { (*ptr).proto().as_js_object_ptr() };
!proto_ptr.is_null() && std::ptr::eq(proto_ptr, boolean_proto)
}
} else {
false
};
if !is_ctor {
return Ok(JsValue::bool(bool_val));
}
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
obj.push_prop(JsValue::bool(bool_val));
Ok(this_val)
}
pub fn boolean_prototype_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.valueOf called on non-Boolean object",
));
}
let obj = unsafe { &*this_val.as_js_object_ptr() };
let boolean_proto = vm.kernel().builtin_world().boolean_proto.as_ptr() as *mut JsObject;
let proto_ptr = obj.proto().as_js_object_ptr();
if proto_ptr.is_null() || !std::ptr::eq(proto_ptr, boolean_proto) {
return Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.valueOf called on non-Boolean object",
));
}
let val = obj.get_prop_at(0);
Ok(val)
}
pub fn boolean_prototype_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.toString called on non-Boolean object",
));
}
let obj = unsafe { &*this_val.as_js_object_ptr() };
let boolean_proto = vm.kernel().builtin_world().boolean_proto.as_ptr() as *mut JsObject;
let proto_ptr = obj.proto().as_js_object_ptr();
if proto_ptr.is_null() || !std::ptr::eq(proto_ptr, boolean_proto) {
return Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.toString called on non-Boolean object",
));
}
let val = obj.get_prop_at(0);
let is_true = if val.is_bool() {
val.as_bool()
} else {
!val.is_null() && !val.is_undefined()
};
if is_true {
Ok(vm.intern("true"))
} else {
Ok(vm.intern("false"))
}
}
+640
View File
@@ -0,0 +1,640 @@
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, Timelike, Utc};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::native::NativeResult;
use crate::vm::Vm;
fn get_timestamp(obj: &JsObject) -> f64 {
obj.get_prop_at(0).as_double()
}
fn set_timestamp(obj: &mut JsObject, ms: f64) {
obj.set_prop_at(0, JsValue::float(ms));
}
fn is_date(vm: &Vm, obj: &JsObject) -> bool {
let date_proto = vm.kernel().builtin_world().date_proto.as_ptr() as *mut JsObject;
if date_proto.is_null() {
return false;
}
let proto_ptr = obj.proto().as_js_object_ptr();
if proto_ptr.is_null() {
return false;
}
std::ptr::eq(proto_ptr, date_proto)
}
fn ensure_date(vm: &mut Vm, obj: &JsObject) -> NativeResult {
if !is_date(vm, obj) {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
Ok(JsValue::undefined())
}
fn dt_from_ms(ms: f64) -> Option<DateTime<Utc>> {
if !ms.is_finite() {
return None;
}
DateTime::from_timestamp_millis(ms as i64)
}
fn naive_from_ms(ms: f64) -> Option<NaiveDateTime> {
if !ms.is_finite() {
return None;
}
dt_from_ms(ms).map(|dt| dt.naive_utc())
}
pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let is_ctor_call = this_val.is_object() && {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let date_proto = vm.kernel().builtin_world().date_proto.as_ptr() as *mut JsObject;
if date_proto.is_null() {
false
} else {
let obj = unsafe { &*ptr };
let proto_ptr = obj.proto().as_js_object_ptr();
if proto_ptr.is_null() {
false
} else {
std::ptr::eq(proto_ptr, date_proto)
}
}
}
};
let timestamp = if args.len() < 2 {
Utc::now().timestamp_millis() as f64
} else {
let val = vm.reg(args[1]);
if val.is_string() {
let s = vm
.kernel()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
let mut ts = f64::NAN;
for fmt in &formats {
if let Ok(ndt) = NaiveDateTime::parse_from_str(&s, fmt) {
ts = ndt.and_utc().timestamp_millis() as f64;
break;
}
}
if ts.is_nan() {
if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
if let Some(ndt) = nd
.and_hms_opt(0, 0, 0)
.and_then(|n| n.and_local_timezone(Utc).earliest())
{
ts = ndt.timestamp_millis() as f64;
}
}
}
ts
} else if val.is_int() || val.is_double() {
if val.is_int() {
val.as_int() as f64
} else {
val.as_double()
}
} else if val.is_object() && {
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
is_date(vm, unsafe { &*ptr })
}
} {
let obj = unsafe { &*val.as_js_object_ptr() };
get_timestamp(obj)
} else {
let y =
coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32;
let m = if args.len() > 2 {
coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as u32
} else {
0
};
let d = if args.len() > 3 {
coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as u32
} else {
1
};
let h = if args.len() > 4 {
coercion::to_number(vm.reg(args[4]), vm.kernel().string_forge().as_ref()) as u32
} else {
0
};
let min = if args.len() > 5 {
coercion::to_number(vm.reg(args[5]), vm.kernel().string_forge().as_ref()) as u32
} else {
0
};
let sec = if args.len() > 6 {
coercion::to_number(vm.reg(args[6]), vm.kernel().string_forge().as_ref()) as u32
} else {
0
};
let ms = if args.len() > 7 {
coercion::to_number(vm.reg(args[7]), vm.kernel().string_forge().as_ref()) as u32
} else {
0
};
NaiveDate::from_ymd_opt(y, m + 1, d)
.and_then(|nd| {
nd.and_hms_milli_opt(h, min, sec, ms)
.and_then(|ndt| ndt.and_local_timezone(Utc).earliest())
})
.map(|dt| dt.timestamp_millis() as f64)
.unwrap_or(f64::NAN)
}
};
if !is_ctor_call {
let s = if timestamp.is_finite() {
dt_from_ms(timestamp)
.map(|dt| dt.to_rfc2822())
.unwrap_or_else(|| "Invalid Date".to_string())
} else {
"Invalid Date".to_string()
};
return Ok(vm.intern(&s));
}
let mut obj = JsObject::new_empty(
EMPTY_SHAPE_ID,
JsValue::from_js_object(vm.kernel().builtin_world().date_proto.as_ptr() as *mut JsObject),
);
obj.set_prop_at(0, JsValue::float(timestamp));
let ptr = vm.epoch().alloc(obj);
Ok(JsValue::from_js_object(ptr))
}
pub fn date_now(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
Ok(JsValue::float(Utc::now().timestamp_millis() as f64))
}
pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return Ok(JsValue::float(f64::NAN));
}
let val = vm.reg(args[1]);
if !val.is_string() {
return Ok(JsValue::float(f64::NAN));
}
let s = vm
.kernel()
.string_forge()
.lookup(val.as_string_index())
.unwrap_or_default();
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
let mut ts = f64::NAN;
for fmt in &formats {
if let Ok(ndt) = NaiveDateTime::parse_from_str(&s, fmt) {
ts = ndt.and_utc().timestamp_millis() as f64;
break;
}
}
if ts.is_nan() {
if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
if let Some(ndt) = nd
.and_hms_opt(0, 0, 0)
.and_then(|n| n.and_local_timezone(Utc).earliest())
{
ts = ndt.timestamp_millis() as f64;
}
}
}
Ok(JsValue::float(ts))
}
pub fn date_utc(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
Ok(JsValue::float(f64::NAN))
}
macro_rules! make_getter {
($name:ident, $f:expr, $df:expr) => {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float($df));
}
match dt_from_ms(ms) {
Some(dt) => Ok(JsValue::float(($f)(dt))),
None => Ok(JsValue::float($df)),
}
}
};
}
macro_rules! make_utc_getter {
($name:ident, $f:expr, $df:expr) => {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float($df));
}
match naive_from_ms(ms) {
Some(ndt) => Ok(JsValue::float(($f)(ndt))),
None => Ok(JsValue::float($df)),
}
}
};
}
make_getter!(
date_get_time,
|dt: DateTime<Utc>| dt.timestamp_millis() as f64,
f64::NAN
);
make_getter!(
date_get_full_year,
|dt: DateTime<Utc>| dt.year() as f64,
f64::NAN
);
make_getter!(
date_get_month,
|dt: DateTime<Utc>| dt.month0() as f64,
f64::NAN
);
make_getter!(date_get_date, |dt: DateTime<Utc>| dt.day() as f64, f64::NAN);
make_getter!(
date_get_day,
|dt: DateTime<Utc>| dt.weekday().num_days_from_sunday() as f64,
f64::NAN
);
make_getter!(
date_get_hours,
|dt: DateTime<Utc>| dt.hour() as f64,
f64::NAN
);
make_getter!(
date_get_minutes,
|dt: DateTime<Utc>| dt.minute() as f64,
f64::NAN
);
make_getter!(
date_get_seconds,
|dt: DateTime<Utc>| dt.second() as f64,
f64::NAN
);
make_getter!(
date_get_milliseconds,
|dt: DateTime<Utc>| dt.timestamp_subsec_millis() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_full_year,
|ndt: NaiveDateTime| ndt.date().year() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_month,
|ndt: NaiveDateTime| ndt.date().month0() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_date,
|ndt: NaiveDateTime| ndt.date().day() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_day,
|ndt: NaiveDateTime| ndt.date().weekday().num_days_from_sunday() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_hours,
|ndt: NaiveDateTime| ndt.time().hour() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_minutes,
|ndt: NaiveDateTime| ndt.time().minute() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_seconds,
|ndt: NaiveDateTime| ndt.time().second() as f64,
f64::NAN
);
make_utc_getter!(
date_get_utc_milliseconds,
|ndt: NaiveDateTime| ndt.time().nanosecond() as f64 / 1_000_000.0,
f64::NAN
);
pub fn date_get_timezone_offset(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let offset_min = local_offset_minutes();
Ok(JsValue::float(offset_min as f64))
}
fn local_offset_minutes() -> i32 {
chrono::Local::now().offset().local_minus_utc() / 60
}
fn get_opt_arg(vm: &Vm, args: &[u8], idx: usize, default: u32) -> u32 {
if args.len() > idx {
coercion::to_number(vm.reg(args[idx]), vm.kernel().string_forge().as_ref()) as u32
} else {
default
}
}
pub fn date_set_time(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let val = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref())
} else {
f64::NAN
};
set_timestamp(obj, val);
Ok(JsValue::float(val))
}
pub fn date_set_full_year(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]);
let v = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let raw = vm.reg(args[0]);
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return Ok(JsValue::float(f64::NAN)),
};
let m = get_opt_arg(vm, args, 2, dt.month0());
let d = get_opt_arg(vm, args, 3, dt.day0());
let nd = dt
.with_year(v as i32)
.and_then(|x| x.with_month0(m))
.and_then(|x| x.with_day0(d))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
Ok(JsValue::float(ts))
}
pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]);
let v = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let raw = vm.reg(args[0]);
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return Ok(JsValue::float(f64::NAN)),
};
let d = get_opt_arg(vm, args, 2, dt.day0());
let nd = dt
.with_month0(v as u32)
.and_then(|x| x.with_day0(d))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
Ok(JsValue::float(ts))
}
pub fn date_set_date(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]);
let v = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let raw = vm.reg(args[0]);
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return Ok(JsValue::float(f64::NAN)),
};
let nd = dt.with_day0(v as u32).unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
Ok(JsValue::float(ts))
}
pub fn date_set_hours(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]);
let v = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let raw = vm.reg(args[0]);
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return Ok(JsValue::float(f64::NAN)),
};
let min = get_opt_arg(vm, args, 2, dt.minute());
let sec = get_opt_arg(vm, args, 3, dt.second());
let ms_arg = get_opt_arg(vm, args, 4, dt.timestamp_subsec_millis());
let nd = dt
.with_hour(v as u32)
.and_then(|x| x.with_minute(min))
.and_then(|x| x.with_second(sec))
.and_then(|x| x.with_nanosecond(ms_arg * 1_000_000))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
Ok(JsValue::float(ts))
}
pub fn date_set_minutes(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]);
let v = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let raw = vm.reg(args[0]);
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return Ok(JsValue::float(f64::NAN)),
};
let sec = get_opt_arg(vm, args, 2, dt.second());
let ms_arg = get_opt_arg(vm, args, 3, dt.timestamp_subsec_millis());
let nd = dt
.with_minute(v as u32)
.and_then(|x| x.with_second(sec))
.and_then(|x| x.with_nanosecond(ms_arg * 1_000_000))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
Ok(JsValue::float(ts))
}
pub fn date_set_seconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]);
let v = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let raw = vm.reg(args[0]);
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return Ok(JsValue::float(f64::NAN)),
};
let ms_arg = get_opt_arg(vm, args, 2, dt.timestamp_subsec_millis());
let nd = dt
.with_second(v as u32)
.and_then(|x| x.with_nanosecond(ms_arg * 1_000_000))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
Ok(JsValue::float(ts))
}
pub fn date_set_milliseconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = vm.reg(args[1]);
let v = coercion::to_number(val, vm.kernel().string_forge().as_ref());
let raw = vm.reg(args[0]);
let obj = unsafe { &mut *raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return Ok(JsValue::float(f64::NAN)),
};
let nd = dt.with_nanosecond(v as u32 * 1_000_000).unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
Ok(JsValue::float(ts))
}
fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &str) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(vm.intern(invalid));
}
let s = match dt_from_ms(ms) {
Some(dt) => dt.format(format_str).to_string(),
None => invalid.to_string(),
};
Ok(vm.intern(&s))
}
pub fn date_to_iso_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%Y-%m-%dT%H:%M:%S%.3fZ", "Invalid Date")
}
pub fn date_to_json(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
if !ms.is_finite() {
return Ok(JsValue::null());
}
let s = match dt_from_ms(ms) {
Some(dt) => dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
None => return Ok(JsValue::null()),
};
Ok(vm.intern(&s))
}
pub fn date_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a %b %d %Y %H:%M:%S %Z %z", "Invalid Date")
}
pub fn date_to_date_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a %b %d %Y", "Invalid Date")
}
pub fn date_to_time_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%H:%M:%S %Z", "Invalid Date")
}
pub fn date_to_utc_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a, %d %b %Y %H:%M:%S GMT", "Invalid Date")
}
pub fn date_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !raw.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on incompatible receiver",
));
}
let obj = unsafe { &*raw.as_js_object_ptr() };
ensure_date(vm, obj)?;
let ms = get_timestamp(obj);
Ok(JsValue::float(ms))
}
+111
View File
@@ -0,0 +1,111 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::builtins::set::SetKey;
use crate::native::NativeResult;
use crate::vm::Vm;
const MAP_PROP_INDEX: u8 = 0;
fn get_map_inner(
vm: &mut Vm,
this_val: JsValue,
) -> Result<*mut indexmap::IndexMap<SetKey, JsValue>, JsValue> {
if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on non-Map object",
));
}
let map_ptr = this_val.as_js_object_ptr();
if map_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(
vm,
"Map internal state invalid",
));
}
let map_obj = unsafe { &*map_ptr };
let inner_val = map_obj.get_prop_at(MAP_PROP_INDEX);
if !inner_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"Map internal state invalid",
));
}
let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexMap<SetKey, JsValue>;
if inner_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(
vm,
"Map internal state invalid",
));
}
Ok(inner_ptr)
}
fn new_map_inner() -> *mut indexmap::IndexMap<SetKey, JsValue> {
Box::into_raw(Box::new(indexmap::IndexMap::new()))
}
fn alloc_map(vm: &mut Vm) -> *mut JsObject {
let map_proto = vm.kernel().builtin_world().map_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(map_proto));
let inner = new_map_inner();
obj.set_prop_at(MAP_PROP_INDEX, JsValue::object(inner as *mut u8));
vm.epoch().alloc(obj)
}
pub fn map_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let map_obj = alloc_map(vm);
Ok(JsValue::from_js_object(map_obj))
}
pub fn map_set(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_map_inner(vm, this_val)?;
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let val = vm.reg(if args.len() > 2 { args[2] } else { 0 });
unsafe {
(*inner).insert(SetKey(key), val);
}
Ok(this_val)
}
pub fn map_get(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_map_inner(vm, this_val)?;
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let found = unsafe { (*inner).get(&SetKey(key)).copied() };
Ok(found.unwrap_or(JsValue::undefined()))
}
pub fn map_has(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_map_inner(vm, this_val)?;
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let found = unsafe { (*inner).contains_key(&SetKey(key)) };
Ok(JsValue::bool(found))
}
pub fn map_delete(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_map_inner(vm, this_val)?;
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let removed = unsafe { (*inner).shift_remove(&SetKey(key)) };
Ok(JsValue::bool(removed.is_some()))
}
pub fn map_clear(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_map_inner(vm, this_val)?;
unsafe {
(*inner).clear();
}
Ok(JsValue::undefined())
}
pub fn map_size(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_map_inner(vm, this_val)?;
Ok(JsValue::float(unsafe { (*inner).len() } as f64))
}
+4
View File
@@ -1,9 +1,13 @@
pub mod array;
pub mod boolean;
pub mod date;
pub mod error;
pub mod function;
pub mod global;
pub mod json;
pub mod map;
pub mod math;
pub mod number;
pub mod object;
pub mod set;
pub mod string;
+145
View File
@@ -0,0 +1,145 @@
use std::hash::{Hash, Hasher};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::native::NativeResult;
use crate::vm::Vm;
const SET_PROP_INDEX: u8 = 0;
#[derive(Clone, Copy)]
pub struct SetKey(pub JsValue);
impl PartialEq for SetKey {
fn eq(&self, other: &Self) -> bool {
if self.0.is_double() && other.0.is_double() {
let a = self.0.as_double();
let b = other.0.as_double();
if a.is_nan() && b.is_nan() {
return true;
}
if a == 0.0 && b == 0.0 {
return true;
}
return a == b;
}
unsafe {
std::mem::transmute::<JsValue, u64>(self.0)
== std::mem::transmute::<JsValue, u64>(other.0)
}
}
}
impl Eq for SetKey {}
impl Hash for SetKey {
fn hash<H: Hasher>(&self, state: &mut H) {
if self.0.is_double() {
let d = self.0.as_double();
if d.is_nan() {
0u64.hash(state);
} else if d == 0.0 {
0.0f64.to_bits().hash(state);
} else {
d.to_bits().hash(state);
}
} else {
unsafe { std::mem::transmute::<JsValue, u64>(self.0).hash(state) }
}
}
}
fn get_set_inner(
vm: &mut Vm,
this_val: JsValue,
) -> Result<*mut indexmap::IndexSet<SetKey>, JsValue> {
if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"called on non-Set object",
));
}
let set_ptr = this_val.as_js_object_ptr();
if set_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(
vm,
"Set internal state invalid",
));
}
let set_obj = unsafe { &*set_ptr };
let inner_val = set_obj.get_prop_at(SET_PROP_INDEX);
if !inner_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"Set internal state invalid",
));
}
let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexSet<SetKey>;
if inner_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(
vm,
"Set internal state invalid",
));
}
Ok(inner_ptr)
}
fn new_set_inner() -> *mut indexmap::IndexSet<SetKey> {
Box::into_raw(Box::new(indexmap::IndexSet::new()))
}
fn alloc_set(vm: &mut Vm) -> *mut JsObject {
let set_proto = vm.kernel().builtin_world().set_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(set_proto));
let inner = new_set_inner();
obj.set_prop_at(SET_PROP_INDEX, JsValue::object(inner as *mut u8));
vm.epoch().alloc(obj)
}
pub fn set_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let set_obj = alloc_set(vm);
Ok(JsValue::from_js_object(set_obj))
}
pub fn set_add(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_set_inner(vm, this_val)?;
let val = vm.reg(if args.len() > 1 { args[1] } else { 0 });
unsafe {
(*inner).insert(SetKey(val));
}
Ok(this_val)
}
pub fn set_has(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_set_inner(vm, this_val)?;
let val = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let found = unsafe { (*inner).contains(&SetKey(val)) };
Ok(JsValue::bool(found))
}
pub fn set_delete(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_set_inner(vm, this_val)?;
let val = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let removed = unsafe { (*inner).shift_remove(&SetKey(val)) };
Ok(JsValue::bool(removed))
}
pub fn set_clear(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_set_inner(vm, this_val)?;
unsafe {
(*inner).clear();
}
Ok(JsValue::undefined())
}
pub fn set_size(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = get_set_inner(vm, this_val)?;
Ok(JsValue::float(unsafe { (*inner).len() } as f64))
}
+510
View File
@@ -709,6 +709,512 @@ fn bind_json(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
global.bump_generation();
}
fn bind_date(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let ctor_ptr = kernel.builtin_world().date_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
let proto_ptr = kernel.builtin_world().date_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
let sf = kernel.string_forge().as_ref();
let sh = kernel.shape_forge().as_ref();
bind_method!(
kernel.builtin_world(),
ctor,
sf,
sh,
"now",
crate::builtins::date::date_now,
0
);
bind_method!(
kernel.builtin_world(),
ctor,
sf,
sh,
"parse",
crate::builtins::date::date_parse,
1
);
bind_method!(
kernel.builtin_world(),
ctor,
sf,
sh,
"UTC",
crate::builtins::date::date_utc,
7
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getTime",
crate::builtins::date::date_get_time,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getFullYear",
crate::builtins::date::date_get_full_year,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getMonth",
crate::builtins::date::date_get_month,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getDate",
crate::builtins::date::date_get_date,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getDay",
crate::builtins::date::date_get_day,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getHours",
crate::builtins::date::date_get_hours,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getMinutes",
crate::builtins::date::date_get_minutes,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getSeconds",
crate::builtins::date::date_get_seconds,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getMilliseconds",
crate::builtins::date::date_get_milliseconds,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCFullYear",
crate::builtins::date::date_get_utc_full_year,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCMonth",
crate::builtins::date::date_get_utc_month,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCDate",
crate::builtins::date::date_get_utc_date,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCDay",
crate::builtins::date::date_get_utc_day,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCHours",
crate::builtins::date::date_get_utc_hours,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCMinutes",
crate::builtins::date::date_get_utc_minutes,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCSeconds",
crate::builtins::date::date_get_utc_seconds,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getUTCMilliseconds",
crate::builtins::date::date_get_utc_milliseconds,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"getTimezoneOffset",
crate::builtins::date::date_get_timezone_offset,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setTime",
crate::builtins::date::date_set_time,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setFullYear",
crate::builtins::date::date_set_full_year,
3
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setMonth",
crate::builtins::date::date_set_month,
2
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setDate",
crate::builtins::date::date_set_date,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setHours",
crate::builtins::date::date_set_hours,
4
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setMinutes",
crate::builtins::date::date_set_minutes,
3
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setSeconds",
crate::builtins::date::date_set_seconds,
2
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"setMilliseconds",
crate::builtins::date::date_set_milliseconds,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"toISOString",
crate::builtins::date::date_to_iso_string,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"toJSON",
crate::builtins::date::date_to_json,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"toString",
crate::builtins::date::date_to_string,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"toDateString",
crate::builtins::date::date_to_date_string,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"toTimeString",
crate::builtins::date::date_to_time_string,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"toUTCString",
crate::builtins::date::date_to_utc_string,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"valueOf",
crate::builtins::date::date_value_of,
0
);
let si_d = kernel.string_forge().intern("Date").0;
let d_shape = kernel.shape_forge().make_shape(global.shape_id(), si_d);
let d_val = JsValue::from_js_object(ctor_ptr);
global.set_shape_id(d_shape);
global.ensure_hash_props().push(Box::new(d_val));
global.bump_generation();
}
fn bind_set(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let ctor_ptr = kernel.builtin_world().set_constructor.as_ptr() as *mut JsObject;
let proto_ptr = kernel.builtin_world().set_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
let sf = kernel.string_forge().as_ref();
let sh = kernel.shape_forge().as_ref();
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"add",
crate::builtins::set::set_add,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"has",
crate::builtins::set::set_has,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"delete",
crate::builtins::set::set_delete,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"clear",
crate::builtins::set::set_clear,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"size",
crate::builtins::set::set_size,
0
);
let si_s = kernel.string_forge().intern("Set").0;
let s_shape = kernel.shape_forge().make_shape(global.shape_id(), si_s);
let s_val = JsValue::from_js_object(ctor_ptr);
global.set_shape_id(s_shape);
global.ensure_hash_props().push(Box::new(s_val));
global.bump_generation();
}
fn bind_map(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
let ctor_ptr = kernel.builtin_world().map_constructor.as_ptr() as *mut JsObject;
let proto_ptr = kernel.builtin_world().map_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
let sf = kernel.string_forge().as_ref();
let sh = kernel.shape_forge().as_ref();
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"set",
crate::builtins::map::map_set,
2
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"get",
crate::builtins::map::map_get,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"has",
crate::builtins::map::map_has,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"delete",
crate::builtins::map::map_delete,
1
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"clear",
crate::builtins::map::map_clear,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"size",
crate::builtins::map::map_size,
0
);
let si_m = kernel.string_forge().intern("Map").0;
let m_shape = kernel.shape_forge().make_shape(global.shape_id(), si_m);
let m_val = JsValue::from_js_object(ctor_ptr);
global.set_shape_id(m_shape);
global.ensure_hash_props().push(Box::new(m_val));
global.bump_generation();
}
fn bind_boolean(kernel: &Arc<OxideKernel>) {
let ctor_ptr = kernel.builtin_world().boolean_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
let proto_ptr = kernel.builtin_world().boolean_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
let sf = kernel.string_forge().as_ref();
let sh = kernel.shape_forge().as_ref();
ctor.set_native_fn(Some(
crate::builtins::boolean::boolean_constructor as *const (),
));
ctor.set_native_arg_count(1);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"valueOf",
crate::builtins::boolean::boolean_prototype_value_of,
0
);
bind_method!(
kernel.builtin_world(),
proto,
sf,
sh,
"toString",
crate::builtins::boolean::boolean_prototype_to_string,
0
);
}
fn bind_function(kernel: &Arc<OxideKernel>) {
let function_methods = FunctionMethods {
call: crate::builtins::function::function_call as *const (),
@@ -733,6 +1239,10 @@ pub fn init_kernel_builtins(kernel: &Arc<OxideKernel>) {
bind_number(kernel, global);
bind_math(kernel, global);
bind_json(kernel, global);
bind_date(kernel, global);
bind_set(kernel, global);
bind_map(kernel, global);
bind_boolean(kernel);
bind_function(kernel);
}