From 432cd814dced0a55e474e3fca75ee623fd27c0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Fri, 5 Jun 2026 03:15:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=20ECMAScript=20?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=BC=BA=E5=88=B6=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 8 ++ Cargo.toml | 2 + crates/oxide_vm/Cargo.toml | 2 + crates/oxide_vm/src/coercion.rs | 176 ++++++++++++++++++++++++++++++++ crates/oxide_vm/src/lib.rs | 1 + 5 files changed, 189 insertions(+) create mode 100644 crates/oxide_vm/src/coercion.rs diff --git a/Cargo.lock b/Cargo.lock index 1299954..cb1dff0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,12 @@ version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +[[package]] +name = "fast-float" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" + [[package]] name = "fastrand" version = "2.4.1" @@ -795,10 +801,12 @@ name = "oxide_vm" version = "0.1.0" dependencies = [ "bumpalo", + "fast-float", "hashbrown 0.14.5", "oxide_compiler", "oxide_kernel", "proptest", + "ryu", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 78eeeb2..1ed332e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,8 @@ bumpalo = "3" hashbrown = "0.14" proptest = "1" rustc-hash = "2" +fast-float = "0.2" +ryu = "1" [workspace.lints.clippy] all = { level = "deny", priority = -1 } diff --git a/crates/oxide_vm/Cargo.toml b/crates/oxide_vm/Cargo.toml index c329ef2..ae4e465 100644 --- a/crates/oxide_vm/Cargo.toml +++ b/crates/oxide_vm/Cargo.toml @@ -11,6 +11,8 @@ oxide_compiler = { path = "../oxide_compiler" } oxide_kernel = { path = "../oxide_kernel" } bumpalo.workspace = true hashbrown.workspace = true +fast-float.workspace = true +ryu.workspace = true [dev-dependencies] proptest.workspace = true diff --git a/crates/oxide_vm/src/coercion.rs b/crates/oxide_vm/src/coercion.rs new file mode 100644 index 0000000..1e98c6b --- /dev/null +++ b/crates/oxide_vm/src/coercion.rs @@ -0,0 +1,176 @@ +use crate::value::JsValue; + +pub fn to_primitive(val: JsValue) -> JsValue { + if val.is_object() { + panic_to_object(); + } + val +} + +pub fn to_number(val: JsValue) -> f64 { + if val.is_int() { + return val.as_int() as f64; + } + if val.is_double() { + return val.as_double(); + } + if val.is_bool() { + return if val.as_bool() { 1.0 } else { 0.0 }; + } + if val.is_null() { + return 0.0; + } + if val.is_undefined() { + return f64::NAN; + } + if val.is_object() { + panic_to_object(); + } + f64::NAN +} + +pub fn to_string(val: JsValue) -> String { + if val.is_int() { + return val.as_int().to_string(); + } + if val.is_double() { + let d = val.as_double(); + if d.is_nan() { + return "NaN".to_string(); + } + if d.is_infinite() { + return if d.is_sign_positive() { + "Infinity".to_string() + } else { + "-Infinity".to_string() + }; + } + let mut buf = ryu::Buffer::new(); + return buf.format(d).to_string(); + } + if val.is_bool() { + return val.as_bool().to_string(); + } + if val.is_null() { + return "null".to_string(); + } + if val.is_undefined() { + return "undefined".to_string(); + } + if val.is_object() { + panic_to_object(); + } + String::new() +} + +pub fn to_boolean(val: JsValue) -> bool { + if val.is_undefined() || val.is_null() { + return false; + } + if val.is_bool() { + return val.as_bool(); + } + if val.is_int() { + return val.as_int() != 0; + } + if val.is_double() { + let d = val.as_double(); + return !(d == 0.0 || d == -0.0 || d.is_nan()); + } + if val.is_object() { + return true; + } + false +} + +pub fn abstract_eq(lhs: JsValue, rhs: JsValue) -> bool { + if lhs.is_null() && rhs.is_undefined() { + return true; + } + if lhs.is_undefined() && rhs.is_null() { + return true; + } + if lhs.is_int() && rhs.is_int() { + return lhs.as_int() == rhs.as_int(); + } + if lhs.is_double() && rhs.is_double() { + return strict_double_eq(lhs.as_double(), rhs.as_double()); + } + if lhs.is_bool() && rhs.is_bool() { + return lhs.as_bool() == rhs.as_bool(); + } + if lhs.is_null() && rhs.is_null() { + return true; + } + if lhs.is_undefined() && rhs.is_undefined() { + return true; + } + if (lhs.is_int() || lhs.is_double()) && (rhs.is_int() || rhs.is_double()) { + return strict_double_eq(to_number(lhs), to_number(rhs)); + } + if (lhs.is_int() || lhs.is_double()) && rhs.is_bool() { + return to_number(lhs) == to_number(rhs); + } + if lhs.is_bool() && (rhs.is_int() || rhs.is_double()) { + return to_number(lhs) == to_number(rhs); + } + if lhs.is_bool() || rhs.is_bool() { + return to_number(lhs) == to_number(rhs); + } + if lhs.is_null() || rhs.is_null() { + return false; + } + if lhs.is_object() || rhs.is_object() { + panic_to_object(); + } + false +} + +pub fn strict_eq(lhs: JsValue, rhs: JsValue) -> bool { + if lhs.is_int() && rhs.is_int() { + return lhs.as_int() == rhs.as_int(); + } + if lhs.is_double() && rhs.is_double() { + return strict_double_eq(lhs.as_double(), rhs.as_double()); + } + if lhs.is_bool() && rhs.is_bool() { + return lhs.as_bool() == rhs.as_bool(); + } + if lhs.is_null() && rhs.is_null() { + return true; + } + if lhs.is_undefined() && rhs.is_undefined() { + return true; + } + if lhs.is_object() && rhs.is_object() { + return lhs.as_ptr() == rhs.as_ptr(); + } + false +} + +fn strict_double_eq(a: f64, b: f64) -> bool { + if a.is_nan() || b.is_nan() { + return false; + } + a == b +} + +pub fn relational_compare(lhs: JsValue, rhs: JsValue) -> Option { + let l = to_number(lhs); + let r = to_number(rhs); + if l.is_nan() || r.is_nan() { + return None; + } + l.partial_cmp(&r).map(|o| o.is_lt()) +} + +pub fn string_concat(lhs: &str, rhs: &str) -> String { + let mut s = String::with_capacity(lhs.len() + rhs.len()); + s.push_str(lhs); + s.push_str(rhs); + s +} + +fn panic_to_object() { + panic!("ToObject not yet implemented (Phase 6)") +} diff --git a/crates/oxide_vm/src/lib.rs b/crates/oxide_vm/src/lib.rs index 5cc2637..b7b76f6 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -1,5 +1,6 @@ #![doc = "OxideJS - Register-based VM with epoch arena memory"] +pub mod coercion; pub mod mem; pub mod value; pub use value::JsValue;