feat: 支持 Date/Map/Set 成员方法
This commit is contained in:
Generated
+220
@@ -29,6 +29,15 @@ version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
@@ -136,6 +145,16 @@ dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
@@ -148,6 +167,17 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.1"
|
||||
@@ -217,6 +247,12 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cow-utils"
|
||||
version = "0.1.3"
|
||||
@@ -325,6 +361,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -337,6 +379,30 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
@@ -405,6 +471,30 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
@@ -435,6 +525,18 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
@@ -842,7 +944,9 @@ name = "oxide_vm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
"fast-float",
|
||||
"indexmap",
|
||||
"memchr",
|
||||
"oxide_compiler",
|
||||
"oxide_kernel",
|
||||
@@ -916,6 +1020,12 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -1244,12 +1354,24 @@ dependencies = [
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
@@ -1432,6 +1554,51 @@ dependencies = [
|
||||
"wit-bindgen 0.51.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
@@ -1497,12 +1664,65 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
||||
@@ -39,6 +39,8 @@ fast-float = "0.2"
|
||||
ryu = "1"
|
||||
memchr = "2"
|
||||
regex = "1"
|
||||
indexmap = "2"
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock"] }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
all = { level = "deny", priority = -1 }
|
||||
|
||||
@@ -195,6 +195,9 @@ impl CompileCtx {
|
||||
"Math",
|
||||
"JSON",
|
||||
"Promise",
|
||||
"Date",
|
||||
"Set",
|
||||
"Map",
|
||||
"parseInt",
|
||||
"parseFloat",
|
||||
"isNaN",
|
||||
|
||||
@@ -120,6 +120,12 @@ pub struct BuiltinWorld {
|
||||
pub eval_error_proto: P<JsObject>,
|
||||
pub math_object: P<JsObject>,
|
||||
pub json_object: P<JsObject>,
|
||||
pub date_constructor: P<JsObject>,
|
||||
pub date_proto: P<JsObject>,
|
||||
pub set_constructor: P<JsObject>,
|
||||
pub set_proto: P<JsObject>,
|
||||
pub map_constructor: P<JsObject>,
|
||||
pub map_proto: P<JsObject>,
|
||||
}
|
||||
|
||||
fn intern_label(string_forge: &StringForge, label: &str) -> u32 {
|
||||
@@ -252,6 +258,31 @@ impl BuiltinWorld {
|
||||
|
||||
let json_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
|
||||
let (date_proto, date_constructor) = make_pair(
|
||||
string_forge,
|
||||
shape_forge,
|
||||
"Date",
|
||||
si_prototype,
|
||||
si_constructor,
|
||||
si_name,
|
||||
);
|
||||
let (set_proto, set_constructor) = make_pair(
|
||||
string_forge,
|
||||
shape_forge,
|
||||
"Set",
|
||||
si_prototype,
|
||||
si_constructor,
|
||||
si_name,
|
||||
);
|
||||
let (map_proto, map_constructor) = make_pair(
|
||||
string_forge,
|
||||
shape_forge,
|
||||
"Map",
|
||||
si_prototype,
|
||||
si_constructor,
|
||||
si_name,
|
||||
);
|
||||
|
||||
/// Overwrite placeholder slots (set up by make_pair) with real values.
|
||||
/// ctor.vec[0] = constructor.prototype -> proto
|
||||
/// proto.vec[0] = proto.constructor -> ctor
|
||||
@@ -278,6 +309,9 @@ impl BuiltinWorld {
|
||||
wire_ctor_proto(&boolean_constructor, &boolean_proto);
|
||||
wire_ctor_proto(&error_constructor, &error_proto);
|
||||
wire_ctor_proto(&symbol_constructor, &symbol_proto);
|
||||
wire_ctor_proto(&date_constructor, &date_proto);
|
||||
wire_ctor_proto(&set_constructor, &set_proto);
|
||||
wire_ctor_proto(&map_constructor, &map_proto);
|
||||
|
||||
Self {
|
||||
object_proto,
|
||||
@@ -304,6 +338,12 @@ impl BuiltinWorld {
|
||||
eval_error_proto,
|
||||
math_object,
|
||||
json_object,
|
||||
date_constructor,
|
||||
date_proto,
|
||||
set_constructor,
|
||||
set_proto,
|
||||
map_constructor,
|
||||
map_proto,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ ryu.workspace = true
|
||||
memchr.workspace = true
|
||||
regex.workspace = true
|
||||
serde_json.workspace = true
|
||||
chrono.workspace = true
|
||||
indexmap.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
proptest.workspace = true
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::builtins::boolean::{
|
||||
boolean_constructor, boolean_prototype_to_string, boolean_prototype_value_of,
|
||||
};
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
|
||||
fn bool_ctor_call(vm: &mut Vm, arg: JsValue) -> JsValue {
|
||||
vm.set_reg(1, arg);
|
||||
boolean_constructor(vm, &[0, 1]).unwrap()
|
||||
}
|
||||
|
||||
fn tbool_wrap(vm: &mut Vm, arg: JsValue) -> JsValue {
|
||||
let proto_ptr = vm.kernel().builtin_world().boolean_proto.as_ptr() as *mut JsObject;
|
||||
let wrapper = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr));
|
||||
let ptr = vm.epoch().alloc(wrapper);
|
||||
let val = JsValue::from_js_object(ptr);
|
||||
vm.set_reg(0, val);
|
||||
vm.set_reg(1, arg);
|
||||
boolean_constructor(vm, &[0, 1]).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tboolean_call_true() {
|
||||
let mut vm = Vm::new();
|
||||
assert!(bool_ctor_call(&mut vm, JsValue::float(1.0)).as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tboolean_call_false() {
|
||||
let mut vm = Vm::new();
|
||||
assert!(!bool_ctor_call(&mut vm, JsValue::float(0.0)).as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tboolean_call_empty_is_false() {
|
||||
let mut vm = Vm::new();
|
||||
let empty = vm.intern("");
|
||||
assert!(!bool_ctor_call(&mut vm, empty).as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tboolean_new_value_of_true() {
|
||||
let mut vm = Vm::new();
|
||||
let wrapper = tbool_wrap(&mut vm, JsValue::float(1.0));
|
||||
vm.set_reg(0, wrapper);
|
||||
assert!(boolean_prototype_value_of(&mut vm, &[0]).unwrap().as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tboolean_new_value_of_false() {
|
||||
let mut vm = Vm::new();
|
||||
let wrapper = tbool_wrap(&mut vm, JsValue::float(0.0));
|
||||
vm.set_reg(0, wrapper);
|
||||
assert!(!boolean_prototype_value_of(&mut vm, &[0]).unwrap().as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tboolean_new_tostring() {
|
||||
let mut vm = Vm::new();
|
||||
let w = tbool_wrap(&mut vm, JsValue::float(1.0));
|
||||
vm.set_reg(0, w);
|
||||
let r = boolean_prototype_to_string(&mut vm, &[0]).unwrap();
|
||||
let s = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(r.as_string_index())
|
||||
.unwrap();
|
||||
assert_eq!(s, "true");
|
||||
|
||||
let w2 = tbool_wrap(&mut vm, JsValue::float(0.0));
|
||||
vm.set_reg(0, w2);
|
||||
let r2 = boolean_prototype_to_string(&mut vm, &[0]).unwrap();
|
||||
let s2 = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(r2.as_string_index())
|
||||
.unwrap();
|
||||
assert_eq!(s2, "false");
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<oxide_types::value::JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_now_returns_number() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Date.now()").unwrap();
|
||||
assert!(result.is_double());
|
||||
assert!(result.as_double() > 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_now_increases() {
|
||||
let mut vm = Vm::new();
|
||||
let t1 = eval(&mut vm, "Date.now()").unwrap().as_double();
|
||||
let t2 = eval(&mut vm, "Date.now()").unwrap().as_double();
|
||||
assert!(t2 >= t1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_parse_returns_nan() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Date.parse('invalid-date')").unwrap();
|
||||
assert!(result.is_double());
|
||||
assert!(result.as_double().is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_parse_iso_format() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Date.parse('2020-01-15')").unwrap();
|
||||
assert!(result.is_double());
|
||||
assert!(!result.as_double().is_nan());
|
||||
assert!(result.as_double() > 0.0);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::builtins::map::{
|
||||
map_clear, map_constructor as new_map, map_delete, map_get, map_has, map_set, map_size,
|
||||
};
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
#[test]
|
||||
fn tmap_constructor_returns_object() {
|
||||
let mut vm = Vm::new();
|
||||
let r = new_map(&mut vm, &[0]).unwrap();
|
||||
assert!(r.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tmap_set_get() {
|
||||
let mut vm = Vm::new();
|
||||
let m = new_map(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, m);
|
||||
vm.set_reg(1, JsValue::float(42.0));
|
||||
vm.set_reg(2, JsValue::float(100.0));
|
||||
map_set(&mut vm, &[0, 1, 2]).unwrap();
|
||||
assert_eq!(map_get(&mut vm, &[0, 1]).unwrap().as_double(), 100.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tmap_get_missing() {
|
||||
let mut vm = Vm::new();
|
||||
let m = new_map(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, m);
|
||||
vm.set_reg(1, JsValue::float(99.0));
|
||||
assert!(map_get(&mut vm, &[0, 1]).unwrap().is_undefined());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tmap_has_check() {
|
||||
let mut vm = Vm::new();
|
||||
let m = new_map(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, m);
|
||||
vm.set_reg(1, JsValue::float(1.0));
|
||||
vm.set_reg(2, JsValue::float(2.0));
|
||||
map_set(&mut vm, &[0, 1, 2]).unwrap();
|
||||
assert!(map_has(&mut vm, &[0, 1]).unwrap().as_bool());
|
||||
assert!(!map_has(&mut vm, &[0, 99]).unwrap().as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tmap_delete_works() {
|
||||
let mut vm = Vm::new();
|
||||
let m = new_map(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, m);
|
||||
vm.set_reg(1, JsValue::float(7.0));
|
||||
vm.set_reg(2, JsValue::float(8.0));
|
||||
map_set(&mut vm, &[0, 1, 2]).unwrap();
|
||||
assert!(map_delete(&mut vm, &[0, 1]).unwrap().as_bool());
|
||||
assert!(!map_has(&mut vm, &[0, 1]).unwrap().as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tmap_size_and_clear() {
|
||||
let mut vm = Vm::new();
|
||||
let m = new_map(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, m);
|
||||
assert_eq!(map_size(&mut vm, &[0]).unwrap().as_double(), 0.0);
|
||||
vm.set_reg(1, JsValue::float(1.0));
|
||||
vm.set_reg(2, JsValue::float(10.0));
|
||||
map_set(&mut vm, &[0, 1, 2]).unwrap();
|
||||
vm.set_reg(1, JsValue::float(2.0));
|
||||
vm.set_reg(2, JsValue::float(20.0));
|
||||
map_set(&mut vm, &[0, 1, 2]).unwrap();
|
||||
assert_eq!(map_size(&mut vm, &[0]).unwrap().as_double(), 2.0);
|
||||
map_clear(&mut vm, &[0]).unwrap();
|
||||
assert_eq!(map_size(&mut vm, &[0]).unwrap().as_double(), 0.0);
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::builtins::set::{
|
||||
set_add, set_clear, set_constructor as new_set, set_delete, set_has, set_size,
|
||||
};
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
#[test]
|
||||
fn tset_constructor_returns_object() {
|
||||
let mut vm = Vm::new();
|
||||
let r = new_set(&mut vm, &[0]).unwrap();
|
||||
assert!(r.is_object());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tset_add_has() {
|
||||
let mut vm = Vm::new();
|
||||
let s = new_set(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, s);
|
||||
vm.set_reg(1, JsValue::float(42.0));
|
||||
let _ = set_add(&mut vm, &[0, 1]).unwrap();
|
||||
assert!(set_has(&mut vm, &[0, 1]).unwrap().as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tset_has_missing() {
|
||||
let mut vm = Vm::new();
|
||||
let s = new_set(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, s);
|
||||
vm.set_reg(1, JsValue::float(99.0));
|
||||
assert!(!set_has(&mut vm, &[0, 1]).unwrap().as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tset_delete_works() {
|
||||
let mut vm = Vm::new();
|
||||
let s = new_set(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, s);
|
||||
vm.set_reg(1, JsValue::float(7.0));
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
assert!(set_delete(&mut vm, &[0, 1]).unwrap().as_bool());
|
||||
assert!(!set_has(&mut vm, &[0, 1]).unwrap().as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tset_size_increases() {
|
||||
let mut vm = Vm::new();
|
||||
let s = new_set(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, s);
|
||||
assert_eq!(set_size(&mut vm, &[0]).unwrap().as_double(), 0.0);
|
||||
vm.set_reg(1, JsValue::float(1.0));
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
vm.set_reg(1, JsValue::float(2.0));
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
assert_eq!(set_size(&mut vm, &[0]).unwrap().as_double(), 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tset_clear_works() {
|
||||
let mut vm = Vm::new();
|
||||
let s = new_set(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, s);
|
||||
vm.set_reg(1, JsValue::float(1.0));
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
set_clear(&mut vm, &[0]).unwrap();
|
||||
assert_eq!(set_size(&mut vm, &[0]).unwrap().as_double(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tset_nan_equality() {
|
||||
let mut vm = Vm::new();
|
||||
let s = new_set(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, s);
|
||||
vm.set_reg(1, JsValue::float(f64::NAN));
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
assert_eq!(set_size(&mut vm, &[0]).unwrap().as_double(), 1.0);
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
assert_eq!(set_size(&mut vm, &[0]).unwrap().as_double(), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tset_signed_zero() {
|
||||
let mut vm = Vm::new();
|
||||
let s = new_set(&mut vm, &[0]).unwrap();
|
||||
vm.set_reg(0, s);
|
||||
vm.set_reg(1, JsValue::float(0.0));
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
vm.set_reg(1, JsValue::float(-0.0));
|
||||
set_add(&mut vm, &[0, 1]).unwrap();
|
||||
assert_eq!(set_size(&mut vm, &[0]).unwrap().as_double(), 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user