feat: 实现 ECMAScript 类型强制转换

This commit is contained in:
爱特每羊队
2026-06-05 03:15:43 +08:00
parent b65a0678f2
commit 432cd814dc
5 changed files with 189 additions and 0 deletions
Generated
+8
View File
@@ -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]]
+2
View File
@@ -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 }
+2
View File
@@ -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
+176
View File
@@ -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<bool> {
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)")
}
+1
View File
@@ -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;