diff --git a/Cargo.lock b/Cargo.lock index 5b2773f..cdcae94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1030,6 +1030,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "oxide_builtins" +version = "0.1.0" +dependencies = [ + "chrono", + "fast-float", + "indexmap", + "memchr", + "oxide_kernel", + "oxide_runtime_api", + "oxide_types", + "regex", + "ryu", + "serde_json", + "unicode-normalization", +] + [[package]] name = "oxide_bytecode" version = "0.1.0" @@ -1107,6 +1124,7 @@ version = "0.1.0" dependencies = [ "oxide_kernel", "oxide_types", + "ryu", ] [[package]] @@ -1143,6 +1161,7 @@ dependencies = [ "fast-float", "indexmap", "memchr", + "oxide_builtins", "oxide_bytecode", "oxide_compiler", "oxide_kernel", diff --git a/Cargo.toml b/Cargo.toml index 71a4b20..1d5af2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "crates/oxide_vm", "crates/oxide_kernel", "crates/oxide_runtime_api", + "crates/oxide_builtins", "crates/oxide_api", "crates/oxide_cli", "crates/oxide_test262", diff --git a/crates/oxide_builtins/Cargo.toml b/crates/oxide_builtins/Cargo.toml new file mode 100644 index 0000000..f1292f5 --- /dev/null +++ b/crates/oxide_builtins/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "oxide_builtins" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true +description = "OxideJS - Generic builtin JS functions parameterized over VmHost" + +[dependencies] +oxide_runtime_api = { path = "../oxide_runtime_api" } +oxide_types = { path = "../oxide_types" } +oxide_kernel = { path = "../oxide_kernel" } +fast-float.workspace = true +ryu.workspace = true +memchr.workspace = true +regex.workspace = true +serde_json.workspace = true +chrono.workspace = true +unicode-normalization.workspace = true +indexmap.workspace = true diff --git a/crates/oxide_vm/src/builtins/array.rs b/crates/oxide_builtins/src/array.rs similarity index 82% rename from crates/oxide_vm/src/builtins/array.rs rename to crates/oxide_builtins/src/array.rs index 4463251..a4bf74d 100644 --- a/crates/oxide_vm/src/builtins/array.rs +++ b/crates/oxide_builtins/src/array.rs @@ -2,9 +2,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, MAX_DENSE_PROPS}; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; macro_rules! array_ptr { ($vm:expr, $args:expr) => {{ @@ -23,11 +21,11 @@ macro_rules! array_ptr_len { }}; } -fn array_type_error(vm: &mut Vm, msg: &str) -> JsValue { - crate::builtins::error::create_type_error(vm, msg) +fn array_type_error(vm: &mut H, msg: &str) -> JsValue { + crate::error::create_type_error(vm, msg) } -fn get_this_array_ref(vm: &mut Vm, val: JsValue) -> Result<*mut JsObject, JsValue> { +fn get_this_array_ref(vm: &mut H, val: JsValue) -> Result<*mut JsObject, JsValue> { if !val.is_object() { return Err(array_type_error(vm, "Array method called on incompatible receiver")); } @@ -42,7 +40,7 @@ fn get_this_array_ref(vm: &mut Vm, val: JsValue) -> Result<*mut JsObject, JsValu Ok(ptr) } -fn require_callback(vm: &mut Vm, callback_val: JsValue) -> Result { +fn require_callback(vm: &mut H, callback_val: JsValue) -> Result { if !callback_val.is_object() { return Err(array_type_error(vm, "callback is not a function")); } @@ -53,7 +51,7 @@ fn require_callback(vm: &mut Vm, callback_val: JsValue) -> Result *mut JsObject { +fn create_new_array(vm: &mut H, n: usize) -> *mut JsObject { let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; vm.alloc_object(JsObject::new_array( EMPTY_SHAPE_ID, @@ -63,25 +61,27 @@ fn create_new_array(vm: &mut Vm, n: usize) -> *mut JsObject { )) } -fn array_length_arg(vm: &mut Vm, value: JsValue) -> Result { - let n = coercion::to_number(value); +fn array_length_arg(vm: &mut H, value: JsValue) -> Result { + let n = oxide_runtime_api::to_number(value); if !n.is_finite() || n < 0.0 || n.fract() != 0.0 || n > MAX_DENSE_PROPS as f64 { - return Err(crate::builtins::error::create_range_error(vm, "Invalid array length")); + return Err(crate::error::create_range_error(vm, "Invalid array length")); } Ok(n as usize) } -fn invoke_native_callback(vm: &mut Vm, callback_val: JsValue, this_val: JsValue, cb_args: &[JsValue]) -> NativeResult { +fn invoke_native_callback( + vm: &mut H, callback_val: JsValue, this_val: JsValue, cb_args: &[JsValue], +) -> NativeResult { if !callback_val.is_object() { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "callback is not a function")); + return NativeResult::Err(crate::error::create_type_error(vm, "callback is not a function")); } let cb_ptr = callback_val.as_js_object_ptr(); if cb_ptr.is_null() { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "callback is not a function")); + return NativeResult::Err(crate::error::create_type_error(vm, "callback is not a function")); } let cb = unsafe { &*cb_ptr }; if !cb.is_function() { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "callback is not a function")); + return NativeResult::Err(crate::error::create_type_error(vm, "callback is not a function")); } match vm.call_function_sync(callback_val, this_val, cb_args) { Ok(value) => NativeResult::Ok(value), @@ -89,31 +89,31 @@ fn invoke_native_callback(vm: &mut Vm, callback_val: JsValue, this_val: JsValue, } } -fn callback_error_from_text(vm: &mut Vm, err: &str) -> JsValue { +fn callback_error_from_text(vm: &mut H, err: &str) -> JsValue { let err = err.strip_prefix("uncaught ").unwrap_or(err); if let Some(msg) = err.strip_prefix("TypeError: ") { - return crate::builtins::error::create_type_error(vm, msg); + return crate::error::create_type_error(vm, msg); } if let Some(msg) = err.strip_prefix("ReferenceError: ") { - return crate::builtins::error::create_reference_error(vm, msg); + return crate::error::create_reference_error(vm, msg); } if let Some(msg) = err.strip_prefix("RangeError: ") { - return crate::builtins::error::create_range_error(vm, msg); + return crate::error::create_range_error(vm, msg); } if let Some(msg) = err.strip_prefix("SyntaxError: ") { - return crate::builtins::error::create_syntax_error(vm, msg); + return crate::error::create_syntax_error(vm, msg); } if let Some(msg) = err.strip_prefix("Error: ") { - return crate::builtins::error::create_error(vm, msg); + return crate::error::create_error(vm, msg); } - crate::builtins::error::create_error(vm, err) + crate::error::create_error(vm, err) } -fn unexpected_tail_call_error(vm: &mut Vm) -> NativeResult { - NativeResult::Err(crate::builtins::error::create_type_error(vm, "unexpected tail call in array callback")) +fn unexpected_tail_call_error(vm: &mut H) -> NativeResult { + NativeResult::Err(crate::error::create_type_error(vm, "unexpected tail call in array callback")) } -pub fn array_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; let proto_val = JsValue::from_js_object(proto); @@ -142,7 +142,7 @@ pub fn array_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(arr)) } -pub fn array_is_array(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_is_array(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); } @@ -157,7 +157,7 @@ pub fn array_is_array(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(unsafe { &*ptr }.is_array())) } -pub fn array_push(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_push(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let mut len = unsafe { &*arr_ptr }.prop_count(); for &arg_reg in args.iter().skip(1) { @@ -169,7 +169,7 @@ pub fn array_push(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(len as i32)) } -pub fn array_pop(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_pop(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count(); @@ -181,17 +181,17 @@ pub fn array_pop(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(last) } -pub fn array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_slice(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; let start = if args.len() > 1 { - (coercion::to_number(vm.reg(args[1])) as i32) as usize + (oxide_runtime_api::to_number(vm.reg(args[1])) as i32) as usize } else { 0 }; let end = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as i32) as usize + (oxide_runtime_api::to_number(vm.reg(args[2])) as i32) as usize } else { n }; @@ -209,13 +209,13 @@ pub fn array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } -pub fn array_splice(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_splice(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let n = arr.prop_count() as usize; let start = if args.len() > 1 { - let v = coercion::to_number(vm.reg(args[1])); + let v = oxide_runtime_api::to_number(vm.reg(args[1])); let s = v as i32; if s < 0 { (n as i32 + s).max(0) as usize @@ -227,7 +227,7 @@ pub fn array_splice(vm: &mut Vm, args: &[u8]) -> NativeResult { }; let delete_count = if args.len() > 2 { - let v = coercion::to_number(vm.reg(args[2])); + let v = oxide_runtime_api::to_number(vm.reg(args[2])); (v as usize).min(n - start) } else { n - start @@ -271,7 +271,7 @@ pub fn array_splice(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(removed_arr)) } -pub fn array_concat(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_concat(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -306,12 +306,12 @@ pub fn array_concat(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } -pub fn array_join(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_join(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; let sep = if args.len() > 1 { - coercion::to_string(vm.reg(args[1])) + oxide_runtime_api::to_string(vm.reg(args[1])) } else { ",".to_string() }; @@ -321,7 +321,7 @@ pub fn array_join(vm: &mut Vm, args: &[u8]) -> NativeResult { if v.is_undefined() || v.is_null() { String::new() } else { - coercion::to_string(v) + oxide_runtime_api::to_string(v) } }) .collect(); @@ -330,7 +330,7 @@ pub fn array_join(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(result_val) } -pub fn array_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_index_of(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -339,14 +339,14 @@ pub fn array_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { } let target = vm.reg(args[1]); for i in 0..n { - if coercion::strict_equality(arr.get_prop_at(i), target) { + if oxide_runtime_api::strict_equality(arr.get_prop_at(i), target) { return NativeResult::Ok(JsValue::int(i as i32)); } } NativeResult::Ok(JsValue::int(-1)) } -pub fn array_includes(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_includes(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -363,14 +363,14 @@ pub fn array_includes(vm: &mut Vm, args: &[u8]) -> NativeResult { return NativeResult::Ok(JsValue::bool(true)); } } - if coercion::strict_equality(elem, target) { + if oxide_runtime_api::strict_equality(elem, target) { return NativeResult::Ok(JsValue::bool(true)); } } NativeResult::Ok(JsValue::bool(false)) } -pub fn array_reverse(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_reverse(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let n = arr.prop_count() as usize; @@ -386,21 +386,21 @@ pub fn array_reverse(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } -pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_flat(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; let depth = if args.len() > 1 { - let n = coercion::to_number(vm.reg(args[1])); + let n = oxide_runtime_api::to_number(vm.reg(args[1])); if !n.is_finite() { - vm.kernel_core.config.max_call_depth + vm.kernel_core().config.max_call_depth } else { (n as i32).max(1) as usize } } else { 1 } - .min(vm.kernel_core.config.max_call_depth); + .min(vm.kernel_core().config.max_call_depth); fn flatten(items: &[JsValue], remaining_depth: usize, seen: &mut Vec<*mut JsObject>) -> Vec { let mut out = Vec::new(); @@ -442,7 +442,7 @@ pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } -pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -463,7 +463,7 @@ pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -490,7 +490,7 @@ pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } -pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -505,7 +505,7 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult { let elem = unsafe { (*arr_ptr).get_prop_at(i) }; match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { NativeResult::Ok(result_val) => { - if coercion::to_boolean(result_val) { + if oxide_runtime_api::to_boolean(result_val) { kept.push(elem); } } @@ -523,7 +523,7 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } -pub fn array_reduce(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if n == 0 && args.len() < 3 { return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value")); @@ -562,7 +562,7 @@ pub fn array_reduce(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(accumulator) } -pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -576,7 +576,7 @@ pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult { let elem = unsafe { (*arr_ptr).get_prop_at(i) }; match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { NativeResult::Ok(result_val) => { - if coercion::to_boolean(result_val) { + if oxide_runtime_api::to_boolean(result_val) { return NativeResult::Ok(elem); } } @@ -587,7 +587,7 @@ pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -601,7 +601,7 @@ pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult { let elem = unsafe { (*arr_ptr).get_prop_at(i) }; match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { NativeResult::Ok(result_val) => { - if coercion::to_boolean(result_val) { + if oxide_runtime_api::to_boolean(result_val) { return NativeResult::Ok(JsValue::bool(true)); } } @@ -612,7 +612,7 @@ pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(false)) } -pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -626,7 +626,7 @@ pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult { let elem = unsafe { (*arr_ptr).get_prop_at(i) }; match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { NativeResult::Ok(result_val) => { - if !coercion::to_boolean(result_val) { + if !oxide_runtime_api::to_boolean(result_val) { return NativeResult::Ok(JsValue::bool(false)); } } @@ -637,7 +637,7 @@ pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(true)) } -pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -681,7 +681,7 @@ pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_arr)) } -pub fn array_shift(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_shift(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count(); @@ -697,7 +697,7 @@ pub fn array_shift(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(first) } -pub fn array_unshift(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_unshift(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count(); @@ -714,18 +714,18 @@ pub fn array_unshift(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(new_len as i32)) } -pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_fill(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count() as usize; let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let start = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as i32).max(0) as usize + (oxide_runtime_api::to_number(vm.reg(args[2])) as i32).max(0) as usize } else { 0 }; let end = if args.len() > 3 { - let e = coercion::to_number(vm.reg(args[3])) as i32; + let e = oxide_runtime_api::to_number(vm.reg(args[3])) as i32; (e as usize).min(len) } else { len @@ -736,7 +736,7 @@ pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } -pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_copy_within(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count() as usize; @@ -744,17 +744,17 @@ pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult { return NativeResult::Ok(vm.reg(args[0])); } let target = if args.len() > 1 { - (coercion::to_number(vm.reg(args[1])) as i32) as usize + (oxide_runtime_api::to_number(vm.reg(args[1])) as i32) as usize } else { 0 }; let start = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as i32) as usize + (oxide_runtime_api::to_number(vm.reg(args[2])) as i32) as usize } else { 0 }; let end = if args.len() > 3 { - (coercion::to_number(vm.reg(args[3])) as i32 as usize).min(len) + (oxide_runtime_api::to_number(vm.reg(args[3])) as i32 as usize).min(len) } else { len }; @@ -770,11 +770,15 @@ pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } -pub fn array_at(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_at(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let len = arr.prop_count() as i32; - let mut index = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) as i32 } else { 0 }; + let mut index = if args.len() > 1 { + oxide_runtime_api::to_number(vm.reg(args[1])) as i32 + } else { + 0 + }; if index < 0 { index += len; } @@ -784,20 +788,20 @@ pub fn array_at(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(arr.get_prop_at(index)) } -pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_last_index_of(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let len = arr.prop_count() as i32; let search = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; for i in (0..len).rev() { - if coercion::strict_equality(arr.get_prop_at(i), search) { + if oxide_runtime_api::strict_equality(arr.get_prop_at(i), search) { return NativeResult::Ok(JsValue::int(i)); } } NativeResult::Ok(JsValue::int(-1)) } -pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -811,7 +815,7 @@ pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult { let elem = unsafe { (*arr_ptr).get_prop_at(i) }; match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) { NativeResult::Ok(r) => { - if coercion::to_boolean(r) { + if oxide_runtime_api::to_boolean(r) { return NativeResult::Ok(JsValue::int(i as i32)); } } @@ -822,7 +826,7 @@ pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } -pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = { let (arr_ptr, len) = array_ptr_len!(vm, args); (arr_ptr, len as i32) @@ -839,7 +843,7 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult { let elem = unsafe { (*arr_ptr).get_prop_at(i) }; match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), vm.reg(args[0])]) { NativeResult::Ok(r) => { - if coercion::to_boolean(r) { + if oxide_runtime_api::to_boolean(r) { return NativeResult::Ok(elem); } } @@ -850,7 +854,7 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn array_reduce_right(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { return NativeResult::Err(array_type_error(vm, "callback is not a function")); @@ -883,7 +887,7 @@ pub fn array_reduce_right(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(acc) } -pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { let arr_ptr = array_ptr!(vm, args); let len = unsafe { (*arr_ptr).prop_count() as usize }; let mut vals: Vec = (0..len).map(|i| unsafe { (*arr_ptr).get_prop_at(i) }).collect(); @@ -908,7 +912,7 @@ pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult { if let Some(callback) = comparator { match invoke_native_callback(vm, callback, JsValue::undefined(), &[*a, *b]) { NativeResult::Ok(result) => { - let n = coercion::to_number(result); + let n = oxide_runtime_api::to_number(result); if n.is_nan() || n == 0.0 { std::cmp::Ordering::Equal } else if n < 0.0 { @@ -922,14 +926,13 @@ pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult { std::cmp::Ordering::Equal } NativeResult::TailCall { .. } => { - sort_error = - Some(crate::builtins::error::create_type_error(vm, "unexpected tail call in array callback")); + sort_error = Some(crate::error::create_type_error(vm, "unexpected tail call in array callback")); std::cmp::Ordering::Equal } } } else { - let sa = coercion::to_string(*a); - let sb = coercion::to_string(*b); + let sa = oxide_runtime_api::to_string(*a); + let sb = oxide_runtime_api::to_string(*b); sa.cmp(&sb) } }); @@ -943,9 +946,9 @@ pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[0])) } -pub fn array_values(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_values(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); - match crate::builtins::iterator::make_iterator_for_value(vm, this_val) { + match crate::iterator::make_iterator_for_value(vm, this_val) { Ok(iterator) => NativeResult::Ok(iterator), Err(err) => NativeResult::Err(err), } diff --git a/crates/oxide_vm/src/builtins/array_buffer.rs b/crates/oxide_builtins/src/array_buffer.rs similarity index 70% rename from crates/oxide_vm/src/builtins/array_buffer.rs rename to crates/oxide_builtins/src/array_buffer.rs index 13059c4..3b928ba 100644 --- a/crates/oxide_vm/src/builtins/array_buffer.rs +++ b/crates/oxide_builtins/src/array_buffer.rs @@ -2,9 +2,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; pub(crate) const MAX_ARRAY_BUFFER_LENGTH: usize = 1 << 30; @@ -17,23 +15,23 @@ macro_rules! native_try { }; } -fn type_error(vm: &mut Vm, msg: &str) -> NativeResult { - NativeResult::Err(crate::builtins::error::create_type_error(vm, msg)) +fn type_error(vm: &mut H, msg: &str) -> NativeResult { + NativeResult::Err(crate::error::create_type_error(vm, msg)) } -fn to_index(vm: &mut Vm, value: JsValue) -> Result { - let n = coercion::to_number(value); +fn to_index(vm: &mut H, value: JsValue) -> Result { + let n = oxide_runtime_api::to_number(value); if n.is_nan() { return Ok(0); } if !n.is_finite() || n < 0.0 || n > MAX_ARRAY_BUFFER_LENGTH as f64 { - return Err(crate::builtins::error::create_range_error(vm, "invalid ArrayBuffer length")); + return Err(crate::error::create_range_error(vm, "invalid ArrayBuffer length")); } Ok(n.trunc() as usize) } -fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize { - let n = coercion::to_number(value); +fn normalize_index(_vm: &mut H, value: JsValue, len: usize) -> usize { + let n = oxide_runtime_api::to_number(value); if n.is_nan() { return 0; } @@ -45,12 +43,12 @@ fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize { } } -fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) { +fn set_named_prop(vm: &mut H, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) { let si = vm.kernel_core().perm_interner().intern(name).0; let _ = vm.define_data_property(obj, si, value, attributes); } -pub(crate) fn new_array_buffer(vm: &mut Vm, data: Vec) -> *mut JsObject { +pub(crate) fn new_array_buffer(vm: &mut H, data: Vec) -> *mut JsObject { let proto = vm.session().builtin_world().array_buffer_proto.as_ptr() as *mut JsObject; let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto)); obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER; @@ -76,7 +74,7 @@ fn array_buffer_vec_ptr(obj: &JsObject) -> Option<*mut Vec> { obj.native_fn().map(|ptr| ptr.as_ptr() as *mut Vec) } -pub(crate) fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) { +pub fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) { let Some(ptr) = array_buffer_vec_ptr(old_obj) else { return; }; @@ -88,7 +86,7 @@ pub(crate) fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObje new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) })); } -pub(crate) fn drop_array_buffer_native(obj: &mut JsObject) -> u64 { +pub fn drop_array_buffer_native(obj: &mut JsObject) -> u64 { let Some(data_ptr) = array_buffer_vec_ptr(obj) else { return 0; }; @@ -102,35 +100,29 @@ pub(crate) fn drop_array_buffer_native(obj: &mut JsObject) -> u64 { bytes as u64 } -pub(crate) fn array_buffer_data_ptr(vm: &mut Vm, this_val: JsValue) -> Result<*mut Vec, JsValue> { +pub(crate) fn array_buffer_data_ptr(vm: &mut H, this_val: JsValue) -> Result<*mut Vec, JsValue> { if !this_val.is_object() { - return Err(crate::builtins::error::create_type_error( - vm, - "ArrayBuffer method called on incompatible receiver", - )); + return Err(crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver")); } let obj_ptr = this_val.as_js_object_ptr(); if obj_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); } let obj = unsafe { &*obj_ptr }; if !obj.is_array_buffer_obj() { - return Err(crate::builtins::error::create_type_error( - vm, - "ArrayBuffer method called on incompatible receiver", - )); + return Err(crate::error::create_type_error(vm, "ArrayBuffer method called on incompatible receiver")); } let Some(data_ptr) = obj.native_fn() else { - return Err(crate::builtins::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); }; let data_ptr = data_ptr.as_ptr() as *mut Vec; if data_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "ArrayBuffer internal state invalid")); + return Err(crate::error::create_type_error(vm, "ArrayBuffer internal state invalid")); } Ok(data_ptr) } -pub fn array_buffer_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_buffer_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let length = if args.len() > 1 { match to_index(vm, vm.reg(args[1])) { Ok(length) => length, @@ -142,13 +134,13 @@ pub fn array_buffer_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_array_buffer(vm, vec![0; length]))) } -pub fn array_buffer_byte_length(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_buffer_byte_length(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let data_ptr = native_try!(array_buffer_data_ptr(vm, this_val)); NativeResult::Ok(JsValue::int(unsafe { (*data_ptr).len() } as i32)) } -pub fn array_buffer_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_buffer_slice(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let data_ptr = native_try!(array_buffer_data_ptr(vm, this_val)); let data = unsafe { &*data_ptr }; @@ -159,7 +151,7 @@ pub fn array_buffer_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(new_array_buffer(vm, data[start..end].to_vec()))) } -pub fn array_buffer_is_view(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_buffer_is_view(vm: &mut H, args: &[u8]) -> NativeResult { let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; if !value.is_object() { return NativeResult::Ok(JsValue::bool(false)); @@ -172,7 +164,7 @@ pub fn array_buffer_is_view(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(obj.is_data_view_obj() || obj.is_typed_array_obj())) } -pub fn array_buffer_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn array_buffer_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if array_buffer_data_ptr(vm, this_val).is_err() { return type_error(vm, "ArrayBuffer.prototype.toString called on incompatible receiver"); diff --git a/crates/oxide_vm/src/builtins/boolean.rs b/crates/oxide_builtins/src/boolean.rs similarity index 83% rename from crates/oxide_vm/src/builtins/boolean.rs rename to crates/oxide_builtins/src/boolean.rs index 41db5b9..c2e4162 100644 --- a/crates/oxide_vm/src/builtins/boolean.rs +++ b/crates/oxide_builtins/src/boolean.rs @@ -1,10 +1,9 @@ use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -pub fn boolean_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn boolean_constructor(vm: &mut H, 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]); @@ -51,20 +50,20 @@ pub fn boolean_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } -pub fn boolean_prototype_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn boolean_prototype_value_of(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if this_val.is_bool() { return NativeResult::Ok(this_val); } if !this_val.is_object() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Boolean.prototype.valueOf called on non-Boolean object", )); } let obj = unsafe { &*this_val.as_js_object_ptr() }; if !obj.is_boolean_obj() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Boolean.prototype.valueOf called on non-Boolean object", )); @@ -73,7 +72,7 @@ pub fn boolean_prototype_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(val) } -pub fn boolean_prototype_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn boolean_prototype_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if this_val.is_bool() { return if this_val.as_bool() { @@ -83,14 +82,14 @@ pub fn boolean_prototype_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { }; } if !this_val.is_object() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Boolean.prototype.toString called on non-Boolean object", )); } let obj = unsafe { &*this_val.as_js_object_ptr() }; if !obj.is_boolean_obj() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Boolean.prototype.toString called on non-Boolean object", )); diff --git a/crates/oxide_vm/src/builtins/data_view.rs b/crates/oxide_builtins/src/data_view.rs similarity index 65% rename from crates/oxide_vm/src/builtins/data_view.rs rename to crates/oxide_builtins/src/data_view.rs index 23481e6..2550048 100644 --- a/crates/oxide_vm/src/builtins/data_view.rs +++ b/crates/oxide_builtins/src/data_view.rs @@ -2,10 +2,9 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; -use crate::builtins::array_buffer::array_buffer_data_ptr; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use crate::array_buffer::array_buffer_data_ptr; + +use oxide_runtime_api::{NativeResult, VmHost}; #[derive(Clone, Copy)] pub(crate) struct DataViewData { @@ -23,69 +22,69 @@ macro_rules! native_try { }; } -fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result { - let n = coercion::to_number(value); +fn to_index(vm: &mut H, value: JsValue, msg: &str) -> Result { + let n = oxide_runtime_api::to_number(value); if n.is_nan() { return Ok(0); } if !n.is_finite() || n < 0.0 { - return Err(crate::builtins::error::create_range_error(vm, msg)); + return Err(crate::error::create_range_error(vm, msg)); } Ok(n.trunc() as usize) } -fn is_little_endian(vm: &mut Vm, args: &[u8], idx: usize) -> bool { - args.get(idx).map(|reg| coercion::to_boolean(vm.reg(*reg))).unwrap_or(false) +fn is_little_endian(vm: &mut H, args: &[u8], idx: usize) -> bool { + args.get(idx) + .map(|reg| oxide_runtime_api::to_boolean(vm.reg(*reg))) + .unwrap_or(false) } -fn numeric_arg(vm: &mut Vm, args: &[u8], idx: usize) -> f64 { - args.get(idx).map(|reg| coercion::to_number(vm.reg(*reg))).unwrap_or(0.0) +fn numeric_arg(vm: &mut H, args: &[u8], idx: usize) -> f64 { + args.get(idx) + .map(|reg| oxide_runtime_api::to_number(vm.reg(*reg))) + .unwrap_or(0.0) } -fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) { +fn set_named_prop(vm: &mut H, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) { let si = vm.kernel_core().perm_interner().intern(name).0; let _ = vm.define_data_property(obj, si, value, attributes); } -fn get_data_view_data(vm: &mut Vm, this_val: JsValue) -> Result { +fn get_data_view_data(vm: &mut H, this_val: JsValue) -> Result { if !this_val.is_object() { - return Err(crate::builtins::error::create_type_error( - vm, - "DataView method called on incompatible receiver", - )); + return Err(crate::error::create_type_error(vm, "DataView method called on incompatible receiver")); } let obj_ptr = this_val.as_js_object_ptr(); if obj_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "DataView internal state invalid")); + return Err(crate::error::create_type_error(vm, "DataView internal state invalid")); } let obj = unsafe { &*obj_ptr }; if !obj.is_data_view_obj() { - return Err(crate::builtins::error::create_type_error( - vm, - "DataView method called on incompatible receiver", - )); + return Err(crate::error::create_type_error(vm, "DataView method called on incompatible receiver")); } let Some(data_ptr) = obj.native_fn() else { - return Err(crate::builtins::error::create_type_error(vm, "DataView internal state invalid")); + return Err(crate::error::create_type_error(vm, "DataView internal state invalid")); }; let data_ptr = data_ptr.as_ptr() as *const DataViewData; if data_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "DataView internal state invalid")); + return Err(crate::error::create_type_error(vm, "DataView internal state invalid")); } Ok(unsafe { *data_ptr }) } -fn checked_absolute_offset(vm: &mut Vm, view: DataViewData, offset: usize, width: usize) -> Result { +fn checked_absolute_offset( + vm: &mut H, view: DataViewData, offset: usize, width: usize, +) -> Result { let Some(end) = offset.checked_add(width) else { - return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds")); + return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")); }; if end > view.byte_length { - return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds")); + return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")); } Ok(view.byte_offset + offset) } -fn read_bytes(vm: &mut Vm, args: &[u8]) -> Result<[u8; N], JsValue> { +fn read_bytes(vm: &mut H, args: &[u8]) -> Result<[u8; N], JsValue> { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = get_data_view_data(vm, this_val)?; let offset = if args.len() > 1 { @@ -97,14 +96,14 @@ fn read_bytes(vm: &mut Vm, args: &[u8]) -> Result<[u8; N], JsVal let buffer_ptr = array_buffer_data_ptr(vm, view.buffer)?; let buffer = unsafe { &*buffer_ptr }; if abs + N > buffer.len() { - return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds")); + return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")); } let mut out = [0u8; N]; out.copy_from_slice(&buffer[abs..abs + N]); Ok(out) } -fn write_bytes(vm: &mut Vm, args: &[u8], bytes: [u8; N]) -> Result<(), JsValue> { +fn write_bytes(vm: &mut H, args: &[u8], bytes: [u8; N]) -> Result<(), JsValue> { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = get_data_view_data(vm, this_val)?; let offset = if args.len() > 1 { @@ -116,15 +115,15 @@ fn write_bytes(vm: &mut Vm, args: &[u8], bytes: [u8; N]) -> Resu let buffer_ptr = array_buffer_data_ptr(vm, view.buffer)?; let buffer = unsafe { &mut *buffer_ptr }; if abs + N > buffer.len() { - return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds")); + return Err(crate::error::create_range_error(vm, "DataView offset out of bounds")); } buffer[abs..abs + N].copy_from_slice(&bytes); Ok(()) } -pub fn data_view_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_constructor(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "DataView requires an ArrayBuffer")); + return NativeResult::Err(crate::error::create_type_error(vm, "DataView requires an ArrayBuffer")); } let buffer = vm.reg(args[1]); let buffer_ptr = native_try!(array_buffer_data_ptr(vm, buffer)); @@ -135,7 +134,7 @@ pub fn data_view_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { 0 }; if byte_offset > buffer_len { - return NativeResult::Err(crate::builtins::error::create_range_error(vm, "DataView byteOffset out of bounds")); + return NativeResult::Err(crate::error::create_range_error(vm, "DataView byteOffset out of bounds")); } let default_len = buffer_len - byte_offset; let byte_length = if args.len() > 3 { @@ -144,7 +143,7 @@ pub fn data_view_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { default_len }; if byte_offset + byte_length > buffer_len { - return NativeResult::Err(crate::builtins::error::create_range_error(vm, "DataView byteLength out of bounds")); + return NativeResult::Err(crate::error::create_range_error(vm, "DataView byteLength out of bounds")); } let proto = vm.session().builtin_world().data_view_proto.as_ptr() as *mut JsObject; @@ -183,7 +182,7 @@ fn data_view_data_ptr(obj: &JsObject) -> Option<*mut DataViewData> { obj.native_fn().map(|ptr| ptr.as_ptr() as *mut DataViewData) } -pub(crate) fn data_view_native_edges(obj: &JsObject) -> Vec { +pub fn data_view_native_edges(obj: &JsObject) -> Vec { let Some(ptr) = data_view_data_ptr(obj) else { return Vec::new(); }; @@ -198,7 +197,7 @@ pub(crate) fn data_view_native_edges(obj: &JsObject) -> Vec { } } -pub(crate) fn clone_data_view_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) +pub fn clone_data_view_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -214,7 +213,7 @@ where new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) })); } -pub(crate) fn rewrite_data_view_native(obj: &mut JsObject, mut rewrite: F) +pub fn rewrite_data_view_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -229,7 +228,7 @@ where } } -pub(crate) fn drop_data_view_native(obj: &mut JsObject) -> u64 { +pub fn drop_data_view_native(obj: &mut JsObject) -> u64 { let Some(ptr) = data_view_data_ptr(obj) else { return 0; }; @@ -241,18 +240,18 @@ pub(crate) fn drop_data_view_native(obj: &mut JsObject) -> u64 { std::mem::size_of::() as u64 } -pub fn data_view_get_int8(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<1>(vm, args)); +pub fn data_view_get_int8(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<1, H>(vm, args)); NativeResult::Ok(JsValue::int(i8::from_ne_bytes(bytes) as i32)) } -pub fn data_view_get_uint8(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<1>(vm, args)); +pub fn data_view_get_uint8(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<1, H>(vm, args)); NativeResult::Ok(JsValue::int(bytes[0] as i32)) } -pub fn data_view_get_int16(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<2>(vm, args)); +pub fn data_view_get_int16(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<2, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { i16::from_le_bytes(bytes) } else { @@ -261,8 +260,8 @@ pub fn data_view_get_int16(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(n as i32)) } -pub fn data_view_get_uint16(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<2>(vm, args)); +pub fn data_view_get_uint16(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<2, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { u16::from_le_bytes(bytes) } else { @@ -271,8 +270,8 @@ pub fn data_view_get_uint16(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(n as i32)) } -pub fn data_view_get_int32(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<4>(vm, args)); +pub fn data_view_get_int32(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<4, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { i32::from_le_bytes(bytes) } else { @@ -281,8 +280,8 @@ pub fn data_view_get_int32(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(n)) } -pub fn data_view_get_uint32(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<4>(vm, args)); +pub fn data_view_get_uint32(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<4, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { u32::from_le_bytes(bytes) } else { @@ -291,8 +290,8 @@ pub fn data_view_get_uint32(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(n as f64)) } -pub fn data_view_get_float32(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<4>(vm, args)); +pub fn data_view_get_float32(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<4, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { f32::from_le_bytes(bytes) } else { @@ -301,8 +300,8 @@ pub fn data_view_get_float32(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(n as f64)) } -pub fn data_view_get_float64(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<8>(vm, args)); +pub fn data_view_get_float64(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<8, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { f64::from_le_bytes(bytes) } else { @@ -311,8 +310,8 @@ pub fn data_view_get_float64(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(n)) } -pub fn data_view_get_big_int64(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<8>(vm, args)); +pub fn data_view_get_big_int64(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<8, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { i64::from_le_bytes(bytes) } else { @@ -321,8 +320,8 @@ pub fn data_view_get_big_int64(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(n as f64)) } -pub fn data_view_get_big_uint64(vm: &mut Vm, args: &[u8]) -> NativeResult { - let bytes = native_try!(read_bytes::<8>(vm, args)); +pub fn data_view_get_big_uint64(vm: &mut H, args: &[u8]) -> NativeResult { + let bytes = native_try!(read_bytes::<8, H>(vm, args)); let n = if is_little_endian(vm, args, 2) { u64::from_le_bytes(bytes) } else { @@ -331,19 +330,19 @@ pub fn data_view_get_big_uint64(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(n as f64)) } -pub fn data_view_set_int8(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_int8(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u8 as i8; native_try!(write_bytes(vm, args, value.to_ne_bytes())); NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_uint8(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_uint8(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u8; native_try!(write_bytes(vm, args, [value])); NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_int16(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_int16(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u16 as i16; let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -354,7 +353,7 @@ pub fn data_view_set_int16(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_uint16(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_uint16(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32 as u16; let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -365,7 +364,7 @@ pub fn data_view_set_uint16(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_int32(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_int32(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i32; let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -376,7 +375,7 @@ pub fn data_view_set_int32(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_uint32(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_uint32(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as u32; let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -387,7 +386,7 @@ pub fn data_view_set_uint32(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_float32(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_float32(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as f32; let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -398,7 +397,7 @@ pub fn data_view_set_float32(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_float64(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_float64(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2); let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -409,7 +408,7 @@ pub fn data_view_set_float64(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_big_int64(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_big_int64(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as i64; let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -420,7 +419,7 @@ pub fn data_view_set_big_int64(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_set_big_uint64(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_set_big_uint64(vm: &mut H, args: &[u8]) -> NativeResult { let value = numeric_arg(vm, args, 2) as u64; let bytes = if is_little_endian(vm, args, 3) { value.to_le_bytes() @@ -431,7 +430,7 @@ pub fn data_view_set_big_uint64(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn data_view_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn data_view_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); native_try!(get_data_view_data(vm, this_val)); NativeResult::Ok(vm.new_string("[object DataView]")) diff --git a/crates/oxide_vm/src/builtins/date.rs b/crates/oxide_builtins/src/date.rs similarity index 79% rename from crates/oxide_vm/src/builtins/date.rs rename to crates/oxide_builtins/src/date.rs index 7be0eac..d8112c7 100644 --- a/crates/oxide_vm/src/builtins/date.rs +++ b/crates/oxide_builtins/src/date.rs @@ -4,9 +4,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; fn get_timestamp(obj: &JsObject) -> f64 { let v = obj.get_prop_at(0); @@ -21,9 +19,9 @@ fn set_timestamp(obj: &mut JsObject, ms: f64) { obj.set_prop_at(0, JsValue::float(ms)); } -fn ensure_date(vm: &mut Vm, obj: &JsObject) -> Result<(), JsValue> { +fn ensure_date(vm: &mut H, obj: &JsObject) -> Result<(), JsValue> { if !obj.is_date_obj() { - return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver")); + return Err(crate::error::create_type_error(vm, "called on incompatible receiver")); } Ok(()) } @@ -37,28 +35,28 @@ macro_rules! native_try { }; } -fn date_this_mut(vm: &mut Vm, args: &[u8]) -> Result<*mut JsObject, JsValue> { +fn date_this_mut(vm: &mut H, args: &[u8]) -> Result<*mut JsObject, JsValue> { 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")); + return Err(crate::error::create_type_error(vm, "called on incompatible receiver")); } let obj_ptr = raw.as_js_object_ptr(); if obj_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver")); + return Err(crate::error::create_type_error(vm, "called on incompatible receiver")); } let obj = unsafe { &*obj_ptr }; ensure_date(vm, obj)?; Ok(obj_ptr) } -fn date_this(vm: &mut Vm, args: &[u8]) -> Result<*const JsObject, JsValue> { +fn date_this(vm: &mut H, args: &[u8]) -> Result<*const JsObject, JsValue> { 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")); + return Err(crate::error::create_type_error(vm, "called on incompatible receiver")); } let obj_ptr = raw.as_js_object_ptr(); if obj_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver")); + return Err(crate::error::create_type_error(vm, "called on incompatible receiver")); } let obj = unsafe { &*obj_ptr }; ensure_date(vm, obj)?; @@ -79,7 +77,7 @@ fn naive_from_ms(ms: f64) -> Option { dt_from_ms(ms).map(|dt| dt.naive_utc()) } -pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_constructor(vm: &mut H, 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(); @@ -104,13 +102,33 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { let timestamp = if args.len() < 2 { Utc::now().timestamp_millis() as f64 } else if args.len() > 2 { - let y = coercion::to_number(vm.reg(args[1])) as i32; - let m = coercion::to_number(vm.reg(args[2])) as u32; - let d = if args.len() > 3 { coercion::to_number(vm.reg(args[3])) as u32 } else { 1 }; - let h = if args.len() > 4 { coercion::to_number(vm.reg(args[4])) as u32 } else { 0 }; - let min = if args.len() > 5 { coercion::to_number(vm.reg(args[5])) as u32 } else { 0 }; - let sec = if args.len() > 6 { coercion::to_number(vm.reg(args[6])) as u32 } else { 0 }; - let ms = if args.len() > 7 { coercion::to_number(vm.reg(args[7])) as u32 } else { 0 }; + let y = oxide_runtime_api::to_number(vm.reg(args[1])) as i32; + let m = oxide_runtime_api::to_number(vm.reg(args[2])) as u32; + let d = if args.len() > 3 { + oxide_runtime_api::to_number(vm.reg(args[3])) as u32 + } else { + 1 + }; + let h = if args.len() > 4 { + oxide_runtime_api::to_number(vm.reg(args[4])) as u32 + } else { + 0 + }; + let min = if args.len() > 5 { + oxide_runtime_api::to_number(vm.reg(args[5])) as u32 + } else { + 0 + }; + let sec = if args.len() > 6 { + oxide_runtime_api::to_number(vm.reg(args[6])) as u32 + } else { + 0 + }; + let ms = if args.len() > 7 { + oxide_runtime_api::to_number(vm.reg(args[7])) as u32 + } else { + 0 + }; NaiveDate::from_ymd_opt(y, m + 1, d) .and_then(|nd| { nd.and_hms_milli_opt(h, min, sec, ms) @@ -182,11 +200,11 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(ptr)) } -pub fn date_now(_vm: &mut Vm, _args: &[u8]) -> NativeResult { +pub fn date_now(_vm: &mut H, _args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(Utc::now().timestamp_millis() as f64)) } -pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_parse(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); } @@ -214,13 +232,13 @@ pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(ts)) } -pub fn date_utc(_vm: &mut Vm, _args: &[u8]) -> NativeResult { +pub fn date_utc(_vm: &mut H, _args: &[u8]) -> NativeResult { 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 { + pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -236,7 +254,7 @@ macro_rules! make_getter { macro_rules! make_utc_getter { ($name:ident, $f:expr, $df:expr) => { - pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult { + pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -277,7 +295,7 @@ make_utc_getter!( f64::NAN ); -pub fn date_get_timezone_offset(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_get_timezone_offset(vm: &mut H, args: &[u8]) -> NativeResult { let _obj = unsafe { &*native_try!(date_this(vm, args)) }; let offset_min = local_offset_minutes(); NativeResult::Ok(JsValue::float(offset_min as f64)) @@ -287,24 +305,28 @@ 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 { +fn get_opt_arg(vm: &H, args: &[u8], idx: usize, default: u32) -> u32 { if args.len() > idx { - coercion::to_number(vm.reg(args[idx])) as u32 + oxide_runtime_api::to_number(vm.reg(args[idx])) as u32 } else { default } } -pub fn date_set_time(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_time(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; - let val = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) } else { f64::NAN }; + let val = if args.len() > 1 { + oxide_runtime_api::to_number(vm.reg(args[1])) + } else { + f64::NAN + }; set_timestamp(obj, val); NativeResult::Ok(JsValue::float(val)) } -pub fn date_set_full_year(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_full_year(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let v = coercion::to_number(val); + let v = oxide_runtime_api::to_number(val); let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -325,9 +347,9 @@ pub fn date_set_full_year(vm: &mut Vm, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } -pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_month(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let v = coercion::to_number(val); + let v = oxide_runtime_api::to_number(val); let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -343,9 +365,9 @@ pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } -pub fn date_set_date(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_date(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let v = coercion::to_number(val); + let v = oxide_runtime_api::to_number(val); let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -360,9 +382,9 @@ pub fn date_set_date(vm: &mut Vm, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } -pub fn date_set_hours(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_hours(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let v = coercion::to_number(val); + let v = oxide_runtime_api::to_number(val); let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -385,9 +407,9 @@ pub fn date_set_hours(vm: &mut Vm, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } -pub fn date_set_minutes(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_minutes(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let v = coercion::to_number(val); + let v = oxide_runtime_api::to_number(val); let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -408,9 +430,9 @@ pub fn date_set_minutes(vm: &mut Vm, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } -pub fn date_set_seconds(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_seconds(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let v = coercion::to_number(val); + let v = oxide_runtime_api::to_number(val); let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -429,9 +451,9 @@ pub fn date_set_seconds(vm: &mut Vm, args: &[u8]) -> NativeResult { set_timestamp(obj, ts); NativeResult::Ok(JsValue::float(ts)) } -pub fn date_set_milliseconds(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_set_milliseconds(vm: &mut H, args: &[u8]) -> NativeResult { let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; - let v = coercion::to_number(val); + let v = oxide_runtime_api::to_number(val); let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -447,7 +469,7 @@ pub fn date_set_milliseconds(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(ts)) } -fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &str) -> NativeResult { +fn date_to_string_inner(vm: &mut H, args: &[u8], format_str: &str, invalid: &str) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -460,11 +482,11 @@ fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &st NativeResult::Ok(vm.new_string(&s)) } -pub fn date_to_iso_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_to_iso_string(vm: &mut H, 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 { +pub fn date_to_json(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); if !ms.is_finite() { @@ -477,23 +499,23 @@ pub fn date_to_json(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&s)) } -pub fn date_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn date_to_string(vm: &mut H, 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 { +pub fn date_to_date_string(vm: &mut H, 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 { +pub fn date_to_time_string(vm: &mut H, 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 { +pub fn date_to_utc_string(vm: &mut H, 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 { +pub fn date_value_of(vm: &mut H, args: &[u8]) -> NativeResult { let obj = unsafe { &*native_try!(date_this(vm, args)) }; let ms = get_timestamp(obj); NativeResult::Ok(JsValue::float(ms)) diff --git a/crates/oxide_builtins/src/error.rs b/crates/oxide_builtins/src/error.rs new file mode 100644 index 0000000..79a9555 --- /dev/null +++ b/crates/oxide_builtins/src/error.rs @@ -0,0 +1,154 @@ +use std::sync::Arc; + +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_runtime_api::{to_string, NativeResult, VmHost}; +use oxide_types::mem::P; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +fn create_error_object(host: &mut H, error_proto_ptr: *mut JsObject, name: &str, message: &str) -> JsValue { + let obj = host + .epoch() + .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(error_proto_ptr))); + + let sf = Arc::clone(host.kernel_core().perm_interner()); + let sh = Arc::clone(host.kernel_core().shape_forge()); + + let si_message = sf.intern("message").0; + let sh_message = sh.make_shape(EMPTY_SHAPE_ID, si_message); + let msg_val = host.new_string(message); + unsafe { + (*obj).set_shape_id(sh_message); + (*obj).push_prop(msg_val); + } + + let si_name = sf.intern("name").0; + let sh_name = sh.make_shape(sh_message, si_name); + let name_val = host.new_string(name); + unsafe { + (*obj).set_shape_id(sh_name); + (*obj).push_prop(name_val); + } + + JsValue::from_js_object(obj) +} + +pub fn create_kind_error(host: &mut H, kind: &str, msg: &str) -> JsValue { + let proto_ptr = match kind { + "TypeError" => P::as_ptr(&host.session().builtin_world().type_error_proto) as *mut JsObject, + "RangeError" => P::as_ptr(&host.session().builtin_world().range_error_proto) as *mut JsObject, + "ReferenceError" => P::as_ptr(&host.session().builtin_world().reference_error_proto) as *mut JsObject, + "SyntaxError" => P::as_ptr(&host.session().builtin_world().syntax_error_proto) as *mut JsObject, + "URIError" => P::as_ptr(&host.session().builtin_world().uri_error_proto) as *mut JsObject, + "EvalError" => P::as_ptr(&host.session().builtin_world().eval_error_proto) as *mut JsObject, + _ => P::as_ptr(&host.session().builtin_world().error_proto) as *mut JsObject, + }; + create_error_object(host, proto_ptr, kind, msg) +} + +pub fn create_type_error(host: &mut H, msg: &str) -> JsValue { + create_kind_error(host, "TypeError", msg) +} + +pub fn create_error(host: &mut H, msg: &str) -> JsValue { + create_kind_error(host, "Error", msg) +} + +pub fn create_reference_error(host: &mut H, msg: &str) -> JsValue { + create_kind_error(host, "ReferenceError", msg) +} + +pub fn create_range_error(host: &mut H, msg: &str) -> JsValue { + create_kind_error(host, "RangeError", msg) +} + +pub fn create_syntax_error(host: &mut H, msg: &str) -> JsValue { + create_kind_error(host, "SyntaxError", msg) +} + +pub fn create_uri_error(host: &mut H, msg: &str) -> JsValue { + create_kind_error(host, "URIError", msg) +} + +fn get_msg(host: &mut H, args: &[u8]) -> String { + if args.len() > 1 { + to_string(host.reg(args[1])) + } else { + String::new() + } +} + +pub fn error_constructor(host: &mut H, args: &[u8]) -> NativeResult { + let msg = get_msg(host, args); + let proto_ptr = P::as_ptr(&host.session().builtin_world().error_proto) as *mut JsObject; + NativeResult::Ok(create_error_object(host, proto_ptr, "Error", &msg)) +} + +pub fn type_error_constructor(host: &mut H, args: &[u8]) -> NativeResult { + let msg = get_msg(host, args); + let proto_ptr = P::as_ptr(&host.session().builtin_world().type_error_proto) as *mut JsObject; + NativeResult::Ok(create_error_object(host, proto_ptr, "TypeError", &msg)) +} + +pub fn reference_error_constructor(host: &mut H, args: &[u8]) -> NativeResult { + let msg = get_msg(host, args); + let proto_ptr = P::as_ptr(&host.session().builtin_world().reference_error_proto) as *mut JsObject; + NativeResult::Ok(create_error_object(host, proto_ptr, "ReferenceError", &msg)) +} + +pub fn range_error_constructor(host: &mut H, args: &[u8]) -> NativeResult { + let msg = get_msg(host, args); + let proto_ptr = P::as_ptr(&host.session().builtin_world().range_error_proto) as *mut JsObject; + NativeResult::Ok(create_error_object(host, proto_ptr, "RangeError", &msg)) +} + +pub fn syntax_error_constructor(host: &mut H, args: &[u8]) -> NativeResult { + let msg = get_msg(host, args); + let proto_ptr = P::as_ptr(&host.session().builtin_world().syntax_error_proto) as *mut JsObject; + NativeResult::Ok(create_error_object(host, proto_ptr, "SyntaxError", &msg)) +} + +pub fn uri_error_constructor(host: &mut H, args: &[u8]) -> NativeResult { + let msg = get_msg(host, args); + let proto_ptr = P::as_ptr(&host.session().builtin_world().uri_error_proto) as *mut JsObject; + NativeResult::Ok(create_error_object(host, proto_ptr, "URIError", &msg)) +} + +pub fn eval_error_constructor(host: &mut H, args: &[u8]) -> NativeResult { + let msg = get_msg(host, args); + let proto_ptr = P::as_ptr(&host.session().builtin_world().eval_error_proto) as *mut JsObject; + NativeResult::Ok(create_error_object(host, proto_ptr, "EvalError", &msg)) +} + +pub fn error_to_string(host: &mut H, args: &[u8]) -> NativeResult { + let this_val = host.reg(args[0]); + let (name, msg) = if this_val.is_object() { + let obj = unsafe { &*this_val.as_js_object_ptr() }; + let name_val = obj + .hash_props_vec() + .and_then(|v| v.first()) + .copied() + .unwrap_or(JsValue::undefined()); + let msg_val = obj + .hash_props_vec() + .and_then(|v| v.get(1)) + .copied() + .unwrap_or(JsValue::undefined()); + ( + host.lookup_str(name_val).unwrap_or_else(|| "Error".to_string()), + host.lookup_str(msg_val).unwrap_or_default(), + ) + } else { + ("Error".to_string(), String::new()) + }; + let result = oxide_runtime_api::format_error_message(&name, &msg); + NativeResult::Ok(host.new_string(&result)) +} + +pub fn error_stack_getter(host: &mut H, _args: &[u8]) -> NativeResult { + let names = host.call_stack_function_names(); + let mut lines: Vec = names.iter().rev().map(|n| format!(" at {} (native)", n)).collect(); + lines.push(" at (native)".to_string()); + let result = lines.join("\n"); + NativeResult::Ok(host.new_string(&result)) +} diff --git a/crates/oxide_vm/src/builtins/function.rs b/crates/oxide_builtins/src/function.rs similarity index 84% rename from crates/oxide_vm/src/builtins/function.rs rename to crates/oxide_builtins/src/function.rs index aa8d6ae..341256d 100644 --- a/crates/oxide_vm/src/builtins/function.rs +++ b/crates/oxide_builtins/src/function.rs @@ -2,10 +2,9 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, NativeFnPtr}; use oxide_types::value::JsValue; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -fn invoke_target(vm: &mut Vm, target_val: JsValue, this_val: JsValue, arg_regs: &[u8]) -> NativeResult { +fn invoke_target(vm: &mut H, target_val: JsValue, this_val: JsValue, arg_regs: &[u8]) -> NativeResult { let args: Vec = arg_regs.iter().map(|&r| vm.reg(r)).collect(); NativeResult::TailCall { callee: target_val, @@ -14,7 +13,7 @@ fn invoke_target(vm: &mut Vm, target_val: JsValue, this_val: JsValue, arg_regs: } } -fn bind_dispatcher(vm: &mut Vm, args: &[u8]) -> NativeResult { +fn bind_dispatcher(vm: &mut H, args: &[u8]) -> NativeResult { let wrapper_val = vm.reg(254); let wrapper = unsafe { &*wrapper_val.as_js_object_ptr() }; let bound_target = wrapper @@ -31,7 +30,7 @@ fn bind_dispatcher(vm: &mut Vm, args: &[u8]) -> NativeResult { invoke_target(vm, bound_target, bound_this, &arg_regs) } -pub fn function_call(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn function_call(vm: &mut H, args: &[u8]) -> NativeResult { if args.is_empty() { return NativeResult::Err(JsValue::undefined()); } @@ -41,7 +40,7 @@ pub fn function_call(vm: &mut Vm, args: &[u8]) -> NativeResult { invoke_target(vm, target_val, this_val, &arg_regs) } -pub fn function_apply(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn function_apply(vm: &mut H, args: &[u8]) -> NativeResult { if args.is_empty() { return NativeResult::Err(JsValue::undefined()); } @@ -81,7 +80,7 @@ pub fn function_apply(vm: &mut Vm, args: &[u8]) -> NativeResult { invoke_target(vm, target_val, this_val, &arg_regs) } -pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn function_bind(vm: &mut H, args: &[u8]) -> NativeResult { if args.is_empty() { return NativeResult::Err(JsValue::undefined()); } @@ -95,7 +94,7 @@ pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult { unsafe { (*wrapper).set_function(true); // SAFETY: bind_dispatcher is a NativeFn fn-item; valid to store as NativeFnPtr. - (*wrapper).set_native_fn(Some(NativeFnPtr::from_raw(bind_dispatcher as *const ()))); + (*wrapper).set_native_fn(Some(NativeFnPtr::from_raw(bind_dispatcher:: as *const ()))); (*wrapper).set_native_arg_count(0); (*wrapper).ensure_hash_props().push(target_val); (*wrapper).ensure_hash_props().push(bound_this); @@ -104,10 +103,10 @@ pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(wrapper)) } -pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn function_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !this_val.is_object() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Function.prototype.toString called on non-function", )); @@ -115,7 +114,7 @@ pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { let func = unsafe { &*this_val.as_js_object_ptr() }; if !func.is_function() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Function.prototype.toString called on non-function", )); @@ -127,8 +126,8 @@ pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { .and_then(|v| vm.lookup_str(v)) .unwrap_or_else(|| { let sub_idx = func.sub_module_index(); - if sub_idx > 0 && (sub_idx as usize) <= vm.sub_modules.len() { - vm.sub_modules[sub_idx as usize - 1].function_name.clone().unwrap_or_default() + if sub_idx > 0 { + vm.sub_module_function_name((sub_idx - 1) as u16) } else { String::new() } diff --git a/crates/oxide_vm/src/builtins/global.rs b/crates/oxide_builtins/src/global.rs similarity index 70% rename from crates/oxide_vm/src/builtins/global.rs rename to crates/oxide_builtins/src/global.rs index 1605137..4c24a76 100644 --- a/crates/oxide_vm/src/builtins/global.rs +++ b/crates/oxide_builtins/src/global.rs @@ -1,15 +1,13 @@ +use oxide_runtime_api::VmHost; mod annex_b; mod uri; -use crate::coercion; -use crate::vm::Vm; - pub use annex_b::{js_escape, js_unescape}; pub use uri::{decode_uri, decode_uri_component, encode_uri, encode_uri_component}; -fn string_arg(vm: &mut Vm, args: &[u8]) -> String { +fn string_arg(vm: &mut H, args: &[u8]) -> String { if args.len() > 1 { - coercion::to_string(vm.reg(args[1])) + oxide_runtime_api::to_string(vm.reg(args[1])) } else { "undefined".to_string() } diff --git a/crates/oxide_vm/src/builtins/global/annex_b.rs b/crates/oxide_builtins/src/global/annex_b.rs similarity index 94% rename from crates/oxide_vm/src/builtins/global/annex_b.rs rename to crates/oxide_builtins/src/global/annex_b.rs index dc7553c..9cbc9d5 100644 --- a/crates/oxide_vm/src/builtins/global/annex_b.rs +++ b/crates/oxide_builtins/src/global/annex_b.rs @@ -1,5 +1,4 @@ -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; const ESCAPE_SAFE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"; @@ -85,12 +84,12 @@ fn unescape_string(input: &str) -> String { out } -pub fn js_escape(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn js_escape(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&escape_string(&input))) } -pub fn js_unescape(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn js_unescape(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&unescape_string(&input))) } diff --git a/crates/oxide_vm/src/builtins/global/uri.rs b/crates/oxide_builtins/src/global/uri.rs similarity index 90% rename from crates/oxide_vm/src/builtins/global/uri.rs rename to crates/oxide_builtins/src/global/uri.rs index 1c5344f..77240b2 100644 --- a/crates/oxide_vm/src/builtins/global/uri.rs +++ b/crates/oxide_builtins/src/global/uri.rs @@ -1,5 +1,4 @@ -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; const URI_UNESCAPED: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()"; const URI_RESERVED: &str = ";/?:@&=+$,#"; @@ -82,21 +81,21 @@ fn decode_uri_string(input: &str, preserve_reserved: bool) -> Result Ok(out) } -fn uri_error(vm: &mut Vm) -> NativeResult { - NativeResult::Err(crate::builtins::error::create_uri_error(vm, URI_ERROR_MESSAGE)) +fn uri_error(vm: &mut H) -> NativeResult { + NativeResult::Err(crate::error::create_uri_error(vm, URI_ERROR_MESSAGE)) } -pub fn encode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn encode_uri(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_SAFE))) } -pub fn encode_uri_component(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn encode_uri_component(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_UNESCAPED))) } -pub fn decode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn decode_uri(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); match decode_uri_string(&input, true) { Ok(decoded) => NativeResult::Ok(vm.new_string(&decoded)), @@ -104,7 +103,7 @@ pub fn decode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn decode_uri_component(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn decode_uri_component(vm: &mut H, args: &[u8]) -> NativeResult { let input = super::string_arg(vm, args); match decode_uri_string(&input, false) { Ok(decoded) => NativeResult::Ok(vm.new_string(&decoded)), diff --git a/crates/oxide_vm/src/builtins/iterator.rs b/crates/oxide_builtins/src/iterator.rs similarity index 76% rename from crates/oxide_vm/src/builtins/iterator.rs rename to crates/oxide_builtins/src/iterator.rs index 3f71ddc..ffe9884 100644 --- a/crates/oxide_vm/src/builtins/iterator.rs +++ b/crates/oxide_builtins/src/iterator.rs @@ -2,18 +2,16 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; const INNER_PROP: &str = "__inner__"; const INDEX_PROP: &str = "__index__"; -pub fn iterator_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult { - NativeResult::Err(crate::builtins::error::create_type_error(vm, "Iterator is not a constructor")) +pub fn iterator_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { + NativeResult::Err(crate::error::create_type_error(vm, "Iterator is not a constructor")) } -pub fn iterator_from(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn iterator_from(vm: &mut H, args: &[u8]) -> NativeResult { let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; match make_iterator_for_value(vm, iterable) { Ok(iterator) => NativeResult::Ok(iterator), @@ -21,7 +19,7 @@ pub fn iterator_from(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub(crate) fn make_iterator_for_value(vm: &mut Vm, value: JsValue) -> Result { +pub fn make_iterator_for_value(vm: &mut H, value: JsValue) -> Result { let inner = get_iterator(vm, value)?; let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject; let wrapper = vm @@ -35,19 +33,16 @@ pub(crate) fn make_iterator_for_value(vm: &mut Vm, value: JsValue) -> Result as *const (), 0); vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn); Ok(JsValue::from_js_object(wrapper)) } -pub fn iterator_wrapper_next(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn iterator_wrapper_next(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if !this_val.is_object() { - return NativeResult::Err(crate::builtins::error::create_type_error( - vm, - "Iterator wrapper next called on non-object", - )); + return NativeResult::Err(crate::error::create_type_error(vm, "Iterator wrapper next called on non-object")); } let wrapper = unsafe { &mut *this_val.as_js_object_ptr() }; @@ -55,12 +50,7 @@ pub fn iterator_wrapper_next(vm: &mut Vm, args: &[u8]) -> NativeResult { let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0; let inner = match vm.ordinary_get(wrapper, inner_si, this_val) { Ok(inner) if !inner.is_undefined() => inner, - _ => { - return NativeResult::Err(crate::builtins::error::create_type_error( - vm, - "Iterator wrapper has no inner iterator", - )) - } + _ => return NativeResult::Err(crate::error::create_type_error(vm, "Iterator wrapper has no inner iterator")), }; if let Some(result) = next_array_like(vm, wrapper, inner, index_si) { @@ -72,18 +62,18 @@ pub fn iterator_wrapper_next(vm: &mut Vm, args: &[u8]) -> NativeResult { let next_si = vm.kernel_core().perm_interner().intern("next").0; let next = match vm.ordinary_get(inner_obj, next_si, inner) { Ok(next) => next, - Err(err) => return NativeResult::Err(crate::builtins::error::create_type_error(vm, &err)), + Err(err) => return NativeResult::Err(crate::error::create_type_error(vm, &err)), }; return match vm.call_function_sync(next, inner, &[]) { Ok(result) => NativeResult::Ok(result), - Err(err) => NativeResult::Err(crate::builtins::error::create_type_error(vm, &err)), + Err(err) => NativeResult::Err(crate::error::create_type_error(vm, &err)), }; } - NativeResult::Err(crate::builtins::error::create_type_error(vm, "value is not iterable")) + NativeResult::Err(crate::error::create_type_error(vm, "value is not iterable")) } -fn get_iterator(vm: &mut Vm, value: JsValue) -> Result { +fn get_iterator(vm: &mut H, value: JsValue) -> Result { if value.is_string() || is_array_value(value) { return Ok(value); } @@ -98,10 +88,10 @@ fn get_iterator(vm: &mut Vm, value: JsValue) -> Result { } } - Err(crate::builtins::error::create_type_error(vm, "value is not iterable")) + Err(crate::error::create_type_error(vm, "value is not iterable")) } -fn next_array_like(vm: &mut Vm, wrapper: &mut JsObject, inner: JsValue, index_si: u32) -> Option { +fn next_array_like(vm: &mut H, wrapper: &mut JsObject, inner: JsValue, index_si: u32) -> Option { if is_array_value(inner) { let index = current_index(vm, wrapper, index_si); let arr = unsafe { &*inner.as_js_object_ptr() }; @@ -115,7 +105,7 @@ fn next_array_like(vm: &mut Vm, wrapper: &mut JsObject, inner: JsValue, index_si if inner.is_string() { let index = current_index(vm, wrapper, index_si); - let source = coercion::to_string(inner); + let source = oxide_runtime_api::to_string(inner); let mut chars = source.chars(); if let Some(ch) = chars.nth(index) { vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32)); @@ -128,7 +118,7 @@ fn next_array_like(vm: &mut Vm, wrapper: &mut JsObject, inner: JsValue, index_si None } -fn current_index(vm: &mut Vm, wrapper: &JsObject, index_si: u32) -> usize { +fn current_index(vm: &mut H, wrapper: &JsObject, index_si: u32) -> usize { match vm.ordinary_get(wrapper, index_si, JsValue::undefined()) { Ok(value) if value.is_int() => value.as_int().max(0) as usize, Ok(value) if value.is_double() => value.as_double().max(0.0) as usize, @@ -136,7 +126,7 @@ fn current_index(vm: &mut Vm, wrapper: &JsObject, index_si: u32) -> usize { } } -fn make_iter_result(vm: &mut Vm, value: JsValue, done: bool) -> JsValue { +fn make_iter_result(vm: &mut H, value: JsValue, done: bool) -> JsValue { let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject; let obj = vm .epoch() @@ -149,7 +139,7 @@ fn make_iter_result(vm: &mut Vm, value: JsValue, done: bool) -> JsValue { JsValue::from_js_object(obj) } -fn make_native_function(vm: &mut Vm, name: &str, native_fn: *const (), arg_count: u8) -> JsValue { +fn make_native_function(vm: &mut H, name: &str, native_fn: *const (), arg_count: u8) -> JsValue { let function_proto = vm.session().builtin_world().function_proto.as_ptr() as *mut JsObject; let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto)); func.set_function(true); diff --git a/crates/oxide_vm/src/builtins/json.rs b/crates/oxide_builtins/src/json.rs similarity index 84% rename from crates/oxide_vm/src/builtins/json.rs rename to crates/oxide_builtins/src/json.rs index 3fda873..b19df61 100644 --- a/crates/oxide_vm/src/builtins/json.rs +++ b/crates/oxide_builtins/src/json.rs @@ -5,20 +5,17 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::builtins::object::walk_own_keys; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use crate::object::walk_own_keys; -pub fn json_parse(vm: &mut Vm, args: &[u8]) -> NativeResult { +use oxide_runtime_api::{NativeResult, VmHost}; + +pub fn json_parse(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { - return NativeResult::Err(crate::builtins::error::create_syntax_error(vm, "JSON.parse requires 1 argument")); + return NativeResult::Err(crate::error::create_syntax_error(vm, "JSON.parse requires 1 argument")); } let val = vm.reg(args[1]); if !val.is_string() { - return NativeResult::Err(crate::builtins::error::create_syntax_error( - vm, - "JSON.parse: argument is not a string", - )); + return NativeResult::Err(crate::error::create_syntax_error(vm, "JSON.parse: argument is not a string")); } let text = { // SAFETY: val is a string value. @@ -27,13 +24,13 @@ pub fn json_parse(vm: &mut Vm, args: &[u8]) -> NativeResult { let parsed: serde_json::Value = match serde_json::from_str(&text) { Ok(v) => v, - Err(e) => return NativeResult::Err(crate::builtins::error::create_syntax_error(vm, &format!("{}", e))), + Err(e) => return NativeResult::Err(crate::error::create_syntax_error(vm, &format!("{}", e))), }; NativeResult::Ok(value_to_jsvalue(vm, &parsed)) } -fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue { +fn value_to_jsvalue(vm: &mut H, val: &serde_json::Value) -> JsValue { match val { serde_json::Value::Null => JsValue::null(), serde_json::Value::Bool(b) => JsValue::bool(*b), @@ -78,7 +75,7 @@ fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue { } } -pub fn json_stringify(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn json_stringify(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); } @@ -89,12 +86,14 @@ pub fn json_stringify(vm: &mut Vm, args: &[u8]) -> NativeResult { let mut visited = HashSet::new(); let mut output = String::new(); if jsvalue_to_json(vm, value, &mut visited, &mut output).is_err() { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "cyclic object value")); + return NativeResult::Err(crate::error::create_type_error(vm, "cyclic object value")); }; NativeResult::Ok(vm.new_string(&output)) } -fn jsvalue_to_json(vm: &Vm, val: JsValue, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> { +fn jsvalue_to_json( + vm: &H, val: JsValue, visited: &mut HashSet<*const u8>, out: &mut String, +) -> Result<(), ()> { if val.is_null() { out.push_str("null"); } else if val.is_undefined() { @@ -163,7 +162,9 @@ fn stringify_string(s: &str, out: &mut String) { out.push('"'); } -fn stringify_object(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> { +fn stringify_object( + vm: &H, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String, +) -> Result<(), ()> { out.push('{'); let keys = walk_own_keys(vm, obj); let mut first = true; @@ -193,7 +194,9 @@ fn stringify_object(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, o Ok(()) } -fn stringify_array(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> { +fn stringify_array( + vm: &H, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String, +) -> Result<(), ()> { out.push('['); let len = obj.prop_vec_len(); for i in 0..len { diff --git a/crates/oxide_vm/src/builtins/mod.rs b/crates/oxide_builtins/src/lib.rs similarity index 100% rename from crates/oxide_vm/src/builtins/mod.rs rename to crates/oxide_builtins/src/lib.rs diff --git a/crates/oxide_vm/src/builtins/map.rs b/crates/oxide_builtins/src/map.rs similarity index 80% rename from crates/oxide_vm/src/builtins/map.rs rename to crates/oxide_builtins/src/map.rs index 0f54d26..49c8674 100644 --- a/crates/oxide_vm/src/builtins/map.rs +++ b/crates/oxide_builtins/src/map.rs @@ -2,9 +2,9 @@ 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::vm::Vm; -use oxide_runtime_api::NativeResult; +use crate::set::SetKey; + +use oxide_runtime_api::{NativeResult, VmHost}; macro_rules! native_try { ($expr:expr) => { @@ -27,20 +27,20 @@ type MapInner = indexmap::IndexMap; /// is created in `new_map_inner()` and is never freed until the process exits (intentional /// leak — lifetime is tied to the epoch). Only one live `*mut` alias exists at a time /// per Map object because native calls are single-threaded. -fn get_map_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut MapInner, JsValue> { +fn get_map_inner(vm: &mut H, this_val: JsValue) -> Result<*mut MapInner, JsValue> { if !this_val.is_object() { - return Err(crate::builtins::error::create_type_error(vm, "called on non-Map object")); + return Err(crate::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")); + return Err(crate::error::create_type_error(vm, "Map internal state invalid")); } // SAFETY: map_ptr is a non-null, aligned pointer to a JsObject bump-allocated in the // current Epoch. It remains valid for the duration of this call (epoch is not reset // during native execution). let map_obj = unsafe { &*map_ptr }; if !map_obj.is_map() { - return Err(crate::builtins::error::create_type_error( + return Err(crate::error::create_type_error( vm, "Map.prototype method called on incompatible receiver", )); @@ -50,7 +50,7 @@ fn get_map_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut MapInner, JsValu // Alignment: IndexMap requires at most 8-byte alignment; the global allocator satisfies this. let inner_ptr = map_obj.native_data() as *mut MapInner; if inner_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid")); + return Err(crate::error::create_type_error(vm, "Map internal state invalid")); } Ok(inner_ptr) } @@ -59,7 +59,7 @@ fn new_map_inner() -> *mut MapInner { Box::into_raw(Box::new(MapInner::new())) } -fn alloc_map(vm: &mut Vm) -> *mut JsObject { +fn alloc_map(vm: &mut H) -> *mut JsObject { let map_proto = vm.session().builtin_world().map_proto.as_ptr() as *mut JsObject; let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(map_proto)); obj.set_map(true); @@ -68,7 +68,7 @@ fn alloc_map(vm: &mut Vm) -> *mut JsObject { vm.alloc_object(obj) } -pub(crate) fn map_native_edges(obj: &JsObject) -> Vec { +pub fn map_native_edges(obj: &JsObject) -> Vec { if !obj.is_map() { return Vec::new(); } @@ -80,12 +80,12 @@ pub(crate) fn map_native_edges(obj: &JsObject) -> Vec { (*inner) .iter() .flat_map(|(key, value)| [key.0, *value]) - .filter(|value| value.is_object()) + .filter(|value: &JsValue| value.is_object()) .collect() } } -pub(crate) fn clone_map_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) +pub fn clone_map_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -108,7 +108,7 @@ where dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); } -pub(crate) fn rewrite_map_native(obj: &mut JsObject, mut rewrite: F) +pub fn rewrite_map_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -130,7 +130,7 @@ where } } -pub(crate) fn drop_map_native(obj: &mut JsObject) -> u64 { +pub fn drop_map_native(obj: &mut JsObject) -> u64 { if !obj.is_map() { return 0; } @@ -139,7 +139,7 @@ pub(crate) fn drop_map_native(obj: &mut JsObject) -> u64 { return 0; } unsafe { - let boxed = Box::from_raw(inner); + let boxed: Box = Box::from_raw(inner); let bytes = std::mem::size_of::() + boxed.capacity() * std::mem::size_of::<(SetKey, JsValue)>(); drop(boxed); obj.set_native_data(std::ptr::null_mut()); @@ -147,12 +147,12 @@ pub(crate) fn drop_map_native(obj: &mut JsObject) -> u64 { } } -pub fn map_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult { +pub fn map_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { let map_obj = alloc_map(vm); NativeResult::Ok(JsValue::from_js_object(map_obj)) } -pub fn map_set(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn map_set(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); let key = vm.reg(if args.len() > 1 { args[1] } else { 0 }); @@ -163,7 +163,7 @@ pub fn map_set(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } -pub fn map_get(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn map_get(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); let key = vm.reg(if args.len() > 1 { args[1] } else { 0 }); @@ -171,7 +171,7 @@ pub fn map_get(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(found.unwrap_or(JsValue::undefined())) } -pub fn map_has(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn map_has(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); let key = vm.reg(if args.len() > 1 { args[1] } else { 0 }); @@ -179,7 +179,7 @@ pub fn map_has(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(found)) } -pub fn map_delete(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn map_delete(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); let key = vm.reg(if args.len() > 1 { args[1] } else { 0 }); @@ -187,7 +187,7 @@ pub fn map_delete(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(removed.is_some())) } -pub fn map_clear(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn map_clear(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); unsafe { @@ -196,7 +196,7 @@ pub fn map_clear(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn map_size(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn map_size(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_map_inner(vm, this_val)); NativeResult::Ok(JsValue::float(unsafe { (*inner).len() } as f64)) diff --git a/crates/oxide_vm/src/builtins/math.rs b/crates/oxide_builtins/src/math.rs similarity index 73% rename from crates/oxide_vm/src/builtins/math.rs rename to crates/oxide_builtins/src/math.rs index c4b709f..2386687 100644 --- a/crates/oxide_vm/src/builtins/math.rs +++ b/crates/oxide_builtins/src/math.rs @@ -1,14 +1,12 @@ use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -fn num(vm: &Vm, reg: u8) -> f64 { - coercion::to_number(vm.reg(reg)) +fn num(vm: &H, reg: u8) -> f64 { + oxide_runtime_api::to_number(vm.reg(reg)) } -fn arg1(vm: &Vm, args: &[u8]) -> f64 { +fn arg1(vm: &H, args: &[u8]) -> f64 { if args.len() < 2 { f64::NAN } else { @@ -16,14 +14,14 @@ fn arg1(vm: &Vm, args: &[u8]) -> f64 { } } -fn arg2(vm: &Vm, args: &[u8]) -> (f64, f64) { +fn arg2(vm: &H, args: &[u8]) -> (f64, f64) { ( if args.len() > 1 { num(vm, args[1]) } else { f64::NAN }, if args.len() > 2 { num(vm, args[2]) } else { f64::NAN }, ) } -pub fn math_abs(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_abs(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); } @@ -37,7 +35,7 @@ pub fn math_abs(vm: &mut Vm, args: &[u8]) -> NativeResult { macro_rules! math_unary { ($name:ident, $op:ident) => { - pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult { + pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(arg1(vm, args).$op())) } }; @@ -67,18 +65,18 @@ math_unary!(math_tan, tan); math_unary!(math_tanh, tanh); math_unary!(math_trunc, trunc); -pub fn math_atan2(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_atan2(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.atan2(b))) } -pub fn math_round(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_round(vm: &mut H, args: &[u8]) -> NativeResult { let x = arg1(vm, args); let r = if x < 0.0 { (x - 0.5).ceil() } else { (x + 0.5).floor() }; NativeResult::Ok(JsValue::float(r)) } -pub fn math_sign(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_sign(vm: &mut H, args: &[u8]) -> NativeResult { let x = arg1(vm, args); if x.is_nan() { NativeResult::Ok(JsValue::float(f64::NAN)) @@ -91,31 +89,31 @@ pub fn math_sign(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn math_clz32(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_clz32(vm: &mut H, args: &[u8]) -> NativeResult { let n = arg1(vm, args) as u32; NativeResult::Ok(JsValue::int(n.leading_zeros() as i32)) } -pub fn math_fround(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_fround(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(arg1(vm, args) as f32 as f64)) } -pub fn math_hypot(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_hypot(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.hypot(b))) } -pub fn math_imul(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_imul(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::int((a as i32).wrapping_mul(b as i32))) } -pub fn math_pow(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_pow(vm: &mut H, args: &[u8]) -> NativeResult { let (a, b) = arg2(vm, args); NativeResult::Ok(JsValue::float(a.powf(b))) } -pub fn math_max(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_max(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NEG_INFINITY)); } @@ -132,7 +130,7 @@ pub fn math_max(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(m)) } -pub fn math_min(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn math_min(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::INFINITY)); } @@ -149,7 +147,7 @@ pub fn math_min(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(m)) } -pub fn math_random(vm: &mut Vm, _args: &[u8]) -> NativeResult { +pub fn math_random(vm: &mut H, _args: &[u8]) -> NativeResult { vm.step_rng(); NativeResult::Ok(JsValue::float(vm.math_rng_value())) } diff --git a/crates/oxide_vm/src/builtins/number.rs b/crates/oxide_builtins/src/number.rs similarity index 76% rename from crates/oxide_vm/src/builtins/number.rs rename to crates/oxide_builtins/src/number.rs index 0912c07..fb0998a 100644 --- a/crates/oxide_vm/src/builtins/number.rs +++ b/crates/oxide_builtins/src/number.rs @@ -1,11 +1,9 @@ use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -pub fn number_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let n = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) } else { 0.0 }; +pub fn number_constructor(vm: &mut H, args: &[u8]) -> NativeResult { + let n = if args.len() > 1 { oxide_runtime_api::to_number(vm.reg(args[1])) } else { 0.0 }; let number_proto = vm.session().builtin_world().number_proto.as_ptr() as *mut oxide_types::object::JsObject; let is_ctor = if let Some(this_reg) = args.first().copied() { let this_val = vm.reg(this_reg); @@ -44,7 +42,7 @@ pub fn number_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn number_is_nan(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn number_is_nan(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); } @@ -56,7 +54,7 @@ pub fn number_is_nan(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn number_is_finite(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn number_is_finite(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); } @@ -67,15 +65,15 @@ pub fn number_is_finite(vm: &mut Vm, args: &[u8]) -> NativeResult { if val.is_double() { return NativeResult::Ok(JsValue::bool(val.as_double().is_finite())); } - let n = coercion::to_number(val); + let n = oxide_runtime_api::to_number(val); NativeResult::Ok(JsValue::bool(n.is_finite())) } -pub fn number_parse_int(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn number_parse_int(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); } - let s = coercion::to_string(vm.reg(args[1])); + let s = oxide_runtime_api::to_string(vm.reg(args[1])); let s = s.trim(); if s.is_empty() { @@ -83,7 +81,7 @@ pub fn number_parse_int(vm: &mut Vm, args: &[u8]) -> NativeResult { } let radix = if args.len() > 2 { - let r = coercion::to_number(vm.reg(args[2])) as i32; + let r = oxide_runtime_api::to_number(vm.reg(args[2])) as i32; if r == 0 { 10 } else { @@ -112,11 +110,11 @@ pub fn number_parse_int(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::float(f64::NAN)) } -pub fn number_parse_float(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn number_parse_float(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::float(f64::NAN)); } - let s = coercion::to_string(vm.reg(args[1])); + let s = oxide_runtime_api::to_string(vm.reg(args[1])); let s = s.trim(); if s.is_empty() { @@ -129,10 +127,10 @@ pub fn number_parse_float(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn number_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { - let n = coercion::to_number(vm.reg(args[0])); +pub fn number_to_string(vm: &mut H, args: &[u8]) -> NativeResult { + let n = oxide_runtime_api::to_number(vm.reg(args[0])); let radix = if args.len() > 1 { - let r = coercion::to_number(vm.reg(args[1])) as u32; + let r = oxide_runtime_api::to_number(vm.reg(args[1])) as u32; r.clamp(2, 36) } else { 10u32 @@ -180,10 +178,10 @@ pub fn number_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn number_to_fixed(vm: &mut Vm, args: &[u8]) -> NativeResult { - let n = coercion::to_number(vm.reg(args[0])); +pub fn number_to_fixed(vm: &mut H, args: &[u8]) -> NativeResult { + let n = oxide_runtime_api::to_number(vm.reg(args[0])); let digits = if args.len() > 1 { - coercion::to_number(vm.reg(args[1])) as usize + oxide_runtime_api::to_number(vm.reg(args[1])) as usize } else { 0usize } @@ -193,23 +191,23 @@ pub fn number_to_fixed(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&formatted)) } -pub fn number_is_integer(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn number_is_integer(vm: &mut H, args: &[u8]) -> NativeResult { let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] }); - let n = coercion::to_number(val); + let n = oxide_runtime_api::to_number(val); NativeResult::Ok(JsValue::bool(n.trunc() == n && n.is_finite())) } -pub fn number_is_safe_integer(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn number_is_safe_integer(vm: &mut H, args: &[u8]) -> NativeResult { let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] }); - let n = coercion::to_number(val); + let n = oxide_runtime_api::to_number(val); let safe = n.trunc() == n && n.is_finite() && n >= -9007199254740991i64 as f64 && n <= 9007199254740991i64 as f64; NativeResult::Ok(JsValue::bool(safe)) } -pub fn number_to_exponential(vm: &mut Vm, args: &[u8]) -> NativeResult { - let n = coercion::to_number(vm.reg(args[0])); +pub fn number_to_exponential(vm: &mut H, args: &[u8]) -> NativeResult { + let n = oxide_runtime_api::to_number(vm.reg(args[0])); let digits = if args.len() > 1 { - coercion::to_number(vm.reg(args[1])) as usize + oxide_runtime_api::to_number(vm.reg(args[1])) as usize } else { 0usize } @@ -224,10 +222,10 @@ pub fn number_to_exponential(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&formatted)) } -pub fn number_to_precision(vm: &mut Vm, args: &[u8]) -> NativeResult { - let n = coercion::to_number(vm.reg(args[0])); +pub fn number_to_precision(vm: &mut H, args: &[u8]) -> NativeResult { + let n = oxide_runtime_api::to_number(vm.reg(args[0])); let precision = if args.len() > 1 { - coercion::to_number(vm.reg(args[1])) as usize + oxide_runtime_api::to_number(vm.reg(args[1])) as usize } else { 0usize } @@ -242,7 +240,7 @@ pub fn number_to_precision(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&formatted)) } -pub fn number_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn number_value_of(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); if this_val.is_int() || this_val.is_double() { return NativeResult::Ok(this_val); @@ -256,7 +254,7 @@ pub fn number_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult { } } } - NativeResult::Err(crate::builtins::error::create_type_error( + NativeResult::Err(crate::error::create_type_error( vm, "Number.prototype.valueOf called on incompatible receiver", )) diff --git a/crates/oxide_vm/src/builtins/object.rs b/crates/oxide_builtins/src/object.rs similarity index 82% rename from crates/oxide_vm/src/builtins/object.rs rename to crates/oxide_builtins/src/object.rs index aeaf671..8758769 100644 --- a/crates/oxide_vm/src/builtins/object.rs +++ b/crates/oxide_builtins/src/object.rs @@ -4,11 +4,9 @@ use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::private_key::is_private_name_key; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -pub(crate) fn walk_own_keys(vm: &Vm, obj: &JsObject) -> Vec<(u32, u32)> { +pub(crate) fn walk_own_keys(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> { let mut keys: Vec<(u32, u32)> = Vec::new(); let shape_id = obj.shape_id(); let mut pos: u32 = 0; @@ -38,12 +36,12 @@ pub(crate) fn walk_own_keys(vm: &Vm, obj: &JsObject) -> Vec<(u32, u32)> { keys } -pub fn object_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult { +pub fn object_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); NativeResult::Ok(JsValue::from_js_object(obj)) } -pub fn object_keys(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_keys(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Err(JsValue::undefined()); } @@ -86,7 +84,7 @@ pub fn object_keys(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(arr)) } -pub fn object_create(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_create(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); } @@ -98,14 +96,14 @@ pub fn object_create(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(obj)) } -pub fn object_assign(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_assign(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "Object.assign requires a target")); + return NativeResult::Err(crate::error::create_type_error(vm, "Object.assign requires a target")); } let target_val = vm.reg(args[1]); - let target_val = match coercion::to_object(target_val, vm) { + let target_val = match oxide_runtime_api::to_object(target_val, vm) { Ok(val) => val, - Err(msg) => return NativeResult::Err(crate::builtins::error::create_type_error(vm, &msg)), + Err(msg) => return NativeResult::Err(crate::error::create_type_error(vm, &msg)), }; let target_ptr = target_val.as_js_object_ptr(); if target_ptr.is_null() { @@ -133,13 +131,13 @@ pub fn object_assign(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(target_val) } -pub fn object_is(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_is(vm: &mut H, args: &[u8]) -> NativeResult { let lhs = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; let rhs = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; - NativeResult::Ok(JsValue::bool(coercion::same_value(lhs, rhs))) + NativeResult::Ok(JsValue::bool(oxide_runtime_api::same_value(lhs, rhs))) } -pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_define_property(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 4 { return NativeResult::Err(JsValue::undefined()); } @@ -159,7 +157,7 @@ pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { if desc_ptr.is_null() { return NativeResult::Err(JsValue::undefined()); } - let prop_name_str = coercion::to_string(vm.reg(args[2])); + let prop_name_str = oxide_runtime_api::to_string(vm.reg(args[2])); let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0; let value_si = vm.kernel_core().perm_interner().intern("value").0; @@ -174,13 +172,17 @@ pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { let get_field = own_field(vm, desc, get_si); let set_field = own_field(vm, desc, set_si); let writable_field = own_field(vm, desc, writable_si); - let enumerable = own_field(vm, desc, enumerable_si).map(coercion::to_boolean).unwrap_or(false); - let configurable = own_field(vm, desc, configurable_si).map(coercion::to_boolean).unwrap_or(false); + let enumerable = own_field(vm, desc, enumerable_si) + .map(oxide_runtime_api::to_boolean) + .unwrap_or(false); + let configurable = own_field(vm, desc, configurable_si) + .map(oxide_runtime_api::to_boolean) + .unwrap_or(false); let has_data = value_field.is_some() || writable_field.is_some(); let has_accessor = get_field.is_some() || set_field.is_some(); if has_data && has_accessor { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Invalid property descriptor: cannot mix data and accessor fields", )); @@ -191,7 +193,7 @@ pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { let get = get_field.unwrap_or(JsValue::undefined()); let set = set_field.unwrap_or(JsValue::undefined()); if (!get.is_undefined() && !is_callable(get)) || (!set.is_undefined() && !is_callable(set)) { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "accessor descriptor get/set must be callable or undefined", )); @@ -200,22 +202,22 @@ pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { .define_accessor_property(obj, si, get, set, PropAttributes::new(false, enumerable, configurable)) .is_err() { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "Cannot define property")); + return NativeResult::Err(crate::error::create_type_error(vm, "Cannot define property")); } } else { let value = value_field.unwrap_or(JsValue::undefined()); - let writable = writable_field.map(coercion::to_boolean).unwrap_or(false); + let writable = writable_field.map(oxide_runtime_api::to_boolean).unwrap_or(false); if vm .define_data_property(obj, si, value, PropAttributes::new(writable, enumerable, configurable)) .is_err() { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "Cannot define property")); + return NativeResult::Err(crate::error::create_type_error(vm, "Cannot define property")); } } NativeResult::Ok(obj_val) } -pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_get_own_property_descriptor(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Err(JsValue::undefined()); } @@ -227,7 +229,7 @@ pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeRes if obj_ptr.is_null() { return NativeResult::Ok(JsValue::undefined()); } - let prop_name_str = coercion::to_string(vm.reg(args[2])); + let prop_name_str = oxide_runtime_api::to_string(vm.reg(args[2])); let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0; let (found_value, found_meta, found) = { @@ -277,7 +279,7 @@ pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeRes NativeResult::Ok(JsValue::from_js_object(desc)) } -fn own_field(vm: &Vm, obj: &JsObject, prop_si: u32) -> Option { +fn own_field(vm: &H, obj: &JsObject, prop_si: u32) -> Option { vm.kernel_core() .shape_forge() .lookup_position(obj.shape_id(), prop_si) @@ -300,26 +302,17 @@ fn push_desc_prop(obj: &mut JsObject, shape_forge: &ShapeForge, prop_si: u32, va obj.push_prop(val); } -fn require_obj_arg(vm: &mut Vm, args: &[u8], fn_name: &str) -> Result<*mut JsObject, JsValue> { +fn require_obj_arg(vm: &mut H, args: &[u8], fn_name: &str) -> Result<*mut JsObject, JsValue> { if args.len() < 2 { - return Err(crate::builtins::error::create_type_error( - vm, - &format!("Object.{fn_name} called on non-object"), - )); + return Err(crate::error::create_type_error(vm, &format!("Object.{fn_name} called on non-object"))); } let val = vm.reg(args[1]); if !val.is_object() { - return Err(crate::builtins::error::create_type_error( - vm, - &format!("Object.{fn_name} called on non-object"), - )); + return Err(crate::error::create_type_error(vm, &format!("Object.{fn_name} called on non-object"))); } let ptr = val.as_js_object_ptr(); if ptr.is_null() { - return Err(crate::builtins::error::create_type_error( - vm, - &format!("Object.{fn_name} called on non-object"), - )); + return Err(crate::error::create_type_error(vm, &format!("Object.{fn_name} called on non-object"))); } Ok(ptr) } @@ -333,7 +326,7 @@ macro_rules! native_try { }; } -pub fn object_freeze(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_freeze(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); } @@ -344,7 +337,7 @@ pub fn object_freeze(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(val) } -pub fn object_seal(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_seal(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); } @@ -355,7 +348,7 @@ pub fn object_seal(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(val) } -pub fn object_prevent_extensions(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_prevent_extensions(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); } @@ -366,7 +359,7 @@ pub fn object_prevent_extensions(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(val) } -pub fn object_is_frozen(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_is_frozen(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(true)); } @@ -377,7 +370,7 @@ pub fn object_is_frozen(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(false)) } -pub fn object_is_sealed(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_is_sealed(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(true)); } @@ -388,12 +381,12 @@ pub fn object_is_sealed(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(false)) } -pub fn object_is_extensible(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_is_extensible(vm: &mut H, args: &[u8]) -> NativeResult { native_try!(require_obj_arg(vm, args, "isExtensible")); NativeResult::Ok(JsValue::bool(true)) } -pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_get_own_property_names(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "getOwnPropertyNames")); let key_names: Vec = { @@ -424,7 +417,7 @@ pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(arr)) } -pub fn object_define_properties(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_define_properties(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Ok(vm.reg(args[1])); } @@ -436,7 +429,7 @@ pub fn object_define_properties(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.reg(args[1])) } -pub fn object_from_entries(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_from_entries(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::null()); } @@ -451,12 +444,12 @@ pub fn object_from_entries(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(obj_ptr)) } -pub fn object_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_get_prototype_of(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "getPrototypeOf")); NativeResult::Ok(unsafe { (*obj_ptr).proto() }) } -pub fn object_has_own(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_has_own(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 3 { return NativeResult::Ok(JsValue::bool(false)); } @@ -466,13 +459,13 @@ pub fn object_has_own(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some())) } -pub fn object_proto_has_own_property(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_proto_has_own_property(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); } let this_val = vm.reg(args[0]); if !this_val.is_object() || this_val.as_js_object_ptr().is_null() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Object.prototype.hasOwnProperty called on non-object", )); @@ -482,13 +475,13 @@ pub fn object_proto_has_own_property(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some())) } -pub fn object_proto_property_is_enumerable(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_proto_property_is_enumerable(vm: &mut H, args: &[u8]) -> NativeResult { if args.len() < 2 { return NativeResult::Ok(JsValue::bool(false)); } let this_val = vm.reg(args[0]); if !this_val.is_object() || this_val.as_js_object_ptr().is_null() { - return NativeResult::Err(crate::builtins::error::create_type_error( + return NativeResult::Err(crate::error::create_type_error( vm, "Object.prototype.propertyIsEnumerable called on non-object", )); @@ -505,7 +498,7 @@ pub fn object_proto_property_is_enumerable(vm: &mut Vm, args: &[u8]) -> NativeRe NativeResult::Ok(JsValue::bool(enumerable)) } -pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_entries(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "entries")); let obj = unsafe { &*obj_ptr }; let keys = walk_own_keys(vm, obj); @@ -540,7 +533,7 @@ pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(arr)) } -pub fn object_values(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn object_values(vm: &mut H, args: &[u8]) -> NativeResult { let obj_ptr = native_try!(require_obj_arg(vm, args, "values")); let obj = unsafe { &*obj_ptr }; let keys = walk_own_keys(vm, obj); diff --git a/crates/oxide_vm/src/builtins/reflect.rs b/crates/oxide_builtins/src/reflect.rs similarity index 82% rename from crates/oxide_vm/src/builtins/reflect.rs rename to crates/oxide_builtins/src/reflect.rs index c8fc1f9..c775db5 100644 --- a/crates/oxide_vm/src/builtins/reflect.rs +++ b/crates/oxide_builtins/src/reflect.rs @@ -2,12 +2,11 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry}; use oxide_types::value::JsValue; -use crate::builtins::object::walk_own_keys; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use crate::object::walk_own_keys; -pub fn reflect_apply(vm: &mut Vm, args: &[u8]) -> NativeResult { +use oxide_runtime_api::{NativeResult, VmHost}; + +pub fn reflect_apply(vm: &mut H, args: &[u8]) -> NativeResult { let target = arg(vm, args, 1); let this_arg = arg(vm, args, 2); let arg_list = arg(vm, args, 3); @@ -23,11 +22,11 @@ pub fn reflect_apply(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn reflect_construct(vm: &mut Vm, _args: &[u8]) -> NativeResult { +pub fn reflect_construct(vm: &mut H, _args: &[u8]) -> NativeResult { type_error(vm, "Reflect.construct not yet supported") } -pub fn reflect_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_define_property(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.defineProperty target is not an object"); @@ -50,8 +49,12 @@ pub fn reflect_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { let get_field = own_field(vm, desc, get_si); let set_field = own_field(vm, desc, set_si); let writable_field = own_field(vm, desc, writable_si); - let enumerable = own_field(vm, desc, enumerable_si).map(coercion::to_boolean).unwrap_or(false); - let configurable = own_field(vm, desc, configurable_si).map(coercion::to_boolean).unwrap_or(false); + let enumerable = own_field(vm, desc, enumerable_si) + .map(oxide_runtime_api::to_boolean) + .unwrap_or(false); + let configurable = own_field(vm, desc, configurable_si) + .map(oxide_runtime_api::to_boolean) + .unwrap_or(false); let has_data = value_field.is_some() || writable_field.is_some(); let has_accessor = get_field.is_some() || set_field.is_some(); @@ -69,13 +72,13 @@ pub fn reflect_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult { vm.define_accessor_property(target, key_si, get, set, PropAttributes::new(false, enumerable, configurable)) } else { let value = value_field.unwrap_or(JsValue::undefined()); - let writable = writable_field.map(coercion::to_boolean).unwrap_or(false); + let writable = writable_field.map(oxide_runtime_api::to_boolean).unwrap_or(false); vm.define_data_property(target, key_si, value, PropAttributes::new(writable, enumerable, configurable)) }; NativeResult::Ok(JsValue::bool(result.is_ok())) } -pub fn reflect_delete_property(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_delete_property(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.deleteProperty target is not an object"); @@ -85,7 +88,7 @@ pub fn reflect_delete_property(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si))) } -pub fn reflect_get(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_get(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.get target is not an object"); @@ -98,15 +101,15 @@ pub fn reflect_get(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn reflect_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_get_own_property_descriptor(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(_) = object_ptr(target_val) else { return type_error(vm, "Reflect.getOwnPropertyDescriptor target is not an object"); }; - crate::builtins::object::object_get_own_property_descriptor(vm, args) + crate::object::object_get_own_property_descriptor(vm, args) } -pub fn reflect_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_get_prototype_of(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.getPrototypeOf target is not an object"); @@ -114,7 +117,7 @@ pub fn reflect_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(unsafe { &*target_ptr }.proto()) } -pub fn reflect_has(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_has(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.has target is not an object"); @@ -123,7 +126,7 @@ pub fn reflect_has(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(vm.resolve_property(unsafe { &*target_ptr }, key_si).is_some())) } -pub fn reflect_is_extensible(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_is_extensible(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.isExtensible target is not an object"); @@ -131,7 +134,7 @@ pub fn reflect_is_extensible(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(unsafe { &*target_ptr }.is_extensible())) } -pub fn reflect_own_keys(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_own_keys(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.ownKeys target is not an object"); @@ -144,7 +147,7 @@ pub fn reflect_own_keys(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(make_string_array(vm, &key_names)) } -pub fn reflect_prevent_extensions(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_prevent_extensions(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.preventExtensions target is not an object"); @@ -153,7 +156,7 @@ pub fn reflect_prevent_extensions(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(true)) } -pub fn reflect_set(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_set(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.set target is not an object"); @@ -168,7 +171,7 @@ pub fn reflect_set(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(vm.ordinary_set(target, key_si, value, receiver).is_ok())) } -pub fn reflect_set_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn reflect_set_prototype_of(vm: &mut H, args: &[u8]) -> NativeResult { let target_val = arg(vm, args, 1); let Some(target_ptr) = object_ptr(target_val) else { return type_error(vm, "Reflect.setPrototypeOf target is not an object"); @@ -180,7 +183,7 @@ pub fn reflect_set_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(unsafe { &mut *target_ptr }.set_proto(proto).is_ok())) } -fn arg(vm: &Vm, args: &[u8], idx: usize) -> JsValue { +fn arg(vm: &H, args: &[u8], idx: usize) -> JsValue { args.get(idx).map(|reg| vm.reg(*reg)).unwrap_or_else(JsValue::undefined) } @@ -196,8 +199,8 @@ fn is_callable(value: JsValue) -> bool { object_ptr(value).is_some_and(|ptr| unsafe { &*ptr }.is_function()) } -fn type_error(vm: &mut Vm, message: &str) -> NativeResult { - NativeResult::Err(crate::builtins::error::create_type_error(vm, message)) +fn type_error(vm: &mut H, message: &str) -> NativeResult { + NativeResult::Err(crate::error::create_type_error(vm, message)) } fn array_like_elements(value: JsValue) -> Option> { @@ -206,14 +209,14 @@ fn array_like_elements(value: JsValue) -> Option> { Some((0..obj.prop_count() as usize).map(|idx| obj.get_prop_at(idx)).collect()) } -fn own_field(vm: &Vm, obj: &JsObject, prop_si: u32) -> Option { +fn own_field(vm: &H, obj: &JsObject, prop_si: u32) -> Option { vm.kernel_core() .shape_forge() .lookup_position(obj.shape_id(), prop_si) .and_then(|pos| (obj.prop_vec_len() > pos as usize).then(|| obj.get_prop_at(pos))) } -fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue { +fn make_string_array(vm: &mut H, parts: &[String]) -> JsValue { let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; let arr = vm.alloc_object(JsObject::new_array( EMPTY_SHAPE_ID, @@ -229,7 +232,7 @@ fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue { JsValue::from_js_object(arr) } -fn delete_own_property(vm: &mut Vm, obj: &mut JsObject, key_si: u32) -> bool { +fn delete_own_property(vm: &mut H, obj: &mut JsObject, key_si: u32) -> bool { let keys = walk_own_keys(vm, obj); let Some((_, delete_pos)) = keys.iter().find(|(si, _)| *si == key_si).copied() else { return true; diff --git a/crates/oxide_vm/src/builtins/regexp.rs b/crates/oxide_builtins/src/regexp.rs similarity index 80% rename from crates/oxide_vm/src/builtins/regexp.rs rename to crates/oxide_builtins/src/regexp.rs index 453b638..948b2b9 100644 --- a/crates/oxide_vm/src/builtins/regexp.rs +++ b/crates/oxide_builtins/src/regexp.rs @@ -2,27 +2,19 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, NativeFnPtr}; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -fn get_regexp_ptr(vm: &mut Vm, args: &[u8]) -> Result<*mut JsObject, JsValue> { +fn get_regexp_ptr(vm: &mut H, args: &[u8]) -> Result<*mut JsObject, JsValue> { let this_val = vm.reg(args[0]); if !this_val.is_object() { - return Err(crate::builtins::error::create_type_error( - vm, - "RegExp.prototype method called on non-object", - )); + return Err(crate::error::create_type_error(vm, "RegExp.prototype method called on non-object")); } let ptr = this_val.as_js_object_ptr(); if ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "null object")); + return Err(crate::error::create_type_error(vm, "null object")); } if !unsafe { &*ptr }.is_regexp_obj() { - return Err(crate::builtins::error::create_type_error( - vm, - "RegExp.prototype method called on non-RegExp object", - )); + return Err(crate::error::create_type_error(vm, "RegExp.prototype method called on non-RegExp object")); } Ok(ptr) } @@ -42,7 +34,7 @@ fn parse_flags(flags: &str) -> (bool, bool, bool) { (global, ignore_case, multi_line) } -fn set_prop(obj: &mut JsObject, name: &str, val: JsValue, vm: &Vm) { +fn set_prop(obj: &mut JsObject, name: &str, val: JsValue, vm: &H) { let si = vm.kernel_core().perm_interner().intern(name).0; let shape_id = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si); obj.set_shape_id(shape_id); @@ -67,15 +59,15 @@ fn set_prop_at(obj: *mut JsObject, idx: usize, val: JsValue) { } } -pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn regexp_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let (pattern, flags) = if args.len() < 2 { (String::new(), String::new()) } else if args.len() < 3 { - let pat = coercion::to_string(vm.reg(args[1])); + let pat = oxide_runtime_api::to_string(vm.reg(args[1])); (pat, String::new()) } else { - let pat = coercion::to_string(vm.reg(args[1])); - let fl = coercion::to_string(vm.reg(args[2])); + let pat = oxide_runtime_api::to_string(vm.reg(args[1])); + let fl = oxide_runtime_api::to_string(vm.reg(args[2])); (pat, fl) }; @@ -101,7 +93,7 @@ pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(re_ptr as *const ()) })); } Err(e) => { - return NativeResult::Err(crate::builtins::error::create_syntax_error( + return NativeResult::Err(crate::error::create_syntax_error( vm, &format!("Invalid regular expression: {e}"), )); @@ -123,7 +115,7 @@ pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(obj_ptr)) } -pub(crate) fn drop_regexp_native(obj: &mut JsObject) -> u64 { +pub fn drop_regexp_native(obj: &mut JsObject) -> u64 { if !obj.is_regexp_obj() { return 0; } @@ -139,7 +131,7 @@ pub(crate) fn drop_regexp_native(obj: &mut JsObject) -> u64 { std::mem::size_of::() as u64 } -pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn regexp_test(vm: &mut H, args: &[u8]) -> NativeResult { let re_ptr = match get_regexp_ptr(vm, args) { Ok(ptr) => ptr, Err(err) => return NativeResult::Err(err), @@ -148,15 +140,15 @@ pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult { let fn_ptr = match re.native_fn() { None => { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "invalid RegExp")); + return NativeResult::Err(crate::error::create_type_error(vm, "invalid RegExp")); } Some(p) => p, }; // SAFETY: fn_ptr holds a Box pointer stored by regexp_constructor. let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) }; - let haystack = coercion::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] })); - let last_index = coercion::to_number(get_prop(re, 0)) as usize; + let haystack = oxide_runtime_api::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] })); + let last_index = oxide_runtime_api::to_number(get_prop(re, 0)) as usize; let is_global = get_prop(re, 3).as_bool(); if is_global { @@ -167,7 +159,7 @@ pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn regexp_exec(vm: &mut H, args: &[u8]) -> NativeResult { let re_ptr = match get_regexp_ptr(vm, args) { Ok(ptr) => ptr, Err(err) => return NativeResult::Err(err), @@ -177,7 +169,7 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult { let re = unsafe { &*re_ptr }; match re.native_fn() { None => { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "invalid RegExp")); + return NativeResult::Err(crate::error::create_type_error(vm, "invalid RegExp")); } Some(p) => p, } @@ -185,11 +177,11 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult { // SAFETY: fn_ptr holds a Box pointer stored by regexp_constructor. let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) }; - let haystack = coercion::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] })); + let haystack = oxide_runtime_api::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] })); let (last_index, is_global) = { let re = unsafe { &*re_ptr }; - let li = coercion::to_number(get_prop(re, 0)) as usize; + let li = oxide_runtime_api::to_number(get_prop(re, 0)) as usize; let g = get_prop(re, 3).as_bool(); (li, g) }; @@ -241,7 +233,7 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn regexp_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn regexp_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let re_ptr = match get_regexp_ptr(vm, args) { Ok(ptr) => ptr, Err(err) => return NativeResult::Err(err), diff --git a/crates/oxide_vm/src/builtins/set.rs b/crates/oxide_builtins/src/set.rs similarity index 80% rename from crates/oxide_vm/src/builtins/set.rs rename to crates/oxide_builtins/src/set.rs index c1862a8..ce4519d 100644 --- a/crates/oxide_vm/src/builtins/set.rs +++ b/crates/oxide_builtins/src/set.rs @@ -4,8 +4,7 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; macro_rules! native_try { ($expr:expr) => { @@ -68,29 +67,26 @@ type SetInner = indexmap::IndexSet; /// native builtin is executing. The `Box` is allocated in `new_set_inner()` /// and is never freed during the Set's lifetime. Only one live `*mut` alias exists per /// Set object at a time because native calls are single-threaded. -fn get_set_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut SetInner, JsValue> { +fn get_set_inner(vm: &mut H, this_val: JsValue) -> Result<*mut SetInner, JsValue> { if !this_val.is_object() { - return Err(crate::builtins::error::create_type_error(vm, "called on non-Set object")); + return Err(crate::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")); + return Err(crate::error::create_type_error(vm, "Set internal state invalid")); } // SAFETY: set_ptr is a non-null, aligned pointer to a JsObject bump-allocated in the // current Epoch. Remains valid for the duration of this call. let set_obj = unsafe { &*set_ptr }; if !set_obj.is_set() { - return Err(crate::builtins::error::create_type_error( - vm, - "Set.prototype.add called on incompatible receiver", - )); + return Err(crate::error::create_type_error(vm, "Set.prototype.add called on incompatible receiver")); } // SAFETY: native_data holds the raw pointer written by `alloc_set`. // The pointer is a valid, heap-allocated `Box>`. // Alignment: IndexSet requires at most 8-byte alignment; the global allocator satisfies this. let inner_ptr = set_obj.native_data() as *mut SetInner; if inner_ptr.is_null() { - return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid")); + return Err(crate::error::create_type_error(vm, "Set internal state invalid")); } Ok(inner_ptr) } @@ -99,7 +95,7 @@ fn new_set_inner() -> *mut SetInner { Box::into_raw(Box::new(SetInner::new())) } -fn alloc_set(vm: &mut Vm) -> *mut JsObject { +fn alloc_set(vm: &mut H) -> *mut JsObject { let set_proto = vm.session().builtin_world().set_proto.as_ptr() as *mut JsObject; let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(set_proto)); obj.set_set(true); @@ -108,7 +104,7 @@ fn alloc_set(vm: &mut Vm) -> *mut JsObject { vm.alloc_object(obj) } -pub(crate) fn set_native_edges(obj: &JsObject) -> Vec { +pub fn set_native_edges(obj: &JsObject) -> Vec { if !obj.is_set() { return Vec::new(); } @@ -116,10 +112,16 @@ pub(crate) fn set_native_edges(obj: &JsObject) -> Vec { if inner.is_null() { return Vec::new(); } - unsafe { (*inner).iter().map(|key| key.0).filter(|value| value.is_object()).collect() } + unsafe { + (*inner) + .iter() + .map(|key| key.0) + .filter(|value: &JsValue| value.is_object()) + .collect() + } } -pub(crate) fn clone_set_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) +pub fn clone_set_native_with_rewrite(src: &JsObject, dst: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -141,7 +143,7 @@ where dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8); } -pub(crate) fn rewrite_set_native(obj: &mut JsObject, mut rewrite: F) +pub fn rewrite_set_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -162,7 +164,7 @@ where } } -pub(crate) fn drop_set_native(obj: &mut JsObject) -> u64 { +pub fn drop_set_native(obj: &mut JsObject) -> u64 { if !obj.is_set() { return 0; } @@ -171,7 +173,7 @@ pub(crate) fn drop_set_native(obj: &mut JsObject) -> u64 { return 0; } unsafe { - let boxed = Box::from_raw(inner); + let boxed: Box = Box::from_raw(inner); let bytes = std::mem::size_of::() + boxed.capacity() * std::mem::size_of::(); drop(boxed); obj.set_native_data(std::ptr::null_mut()); @@ -179,12 +181,12 @@ pub(crate) fn drop_set_native(obj: &mut JsObject) -> u64 { } } -pub fn set_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult { +pub fn set_constructor(vm: &mut H, _args: &[u8]) -> NativeResult { let set_obj = alloc_set(vm); NativeResult::Ok(JsValue::from_js_object(set_obj)) } -pub fn set_add(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn set_add(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); let val = vm.reg(if args.len() > 1 { args[1] } else { 0 }); @@ -194,7 +196,7 @@ pub fn set_add(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } -pub fn set_has(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn set_has(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); let val = vm.reg(if args.len() > 1 { args[1] } else { 0 }); @@ -202,7 +204,7 @@ pub fn set_has(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(found)) } -pub fn set_delete(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn set_delete(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); let val = vm.reg(if args.len() > 1 { args[1] } else { 0 }); @@ -210,7 +212,7 @@ pub fn set_delete(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(removed)) } -pub fn set_clear(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn set_clear(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); unsafe { @@ -219,7 +221,7 @@ pub fn set_clear(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn set_size(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn set_size(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let inner = native_try!(get_set_inner(vm, this_val)); NativeResult::Ok(JsValue::float(unsafe { (*inner).len() } as f64)) diff --git a/crates/oxide_vm/src/builtins/string.rs b/crates/oxide_builtins/src/string.rs similarity index 79% rename from crates/oxide_vm/src/builtins/string.rs rename to crates/oxide_builtins/src/string.rs index 3b7782a..685a420 100644 --- a/crates/oxide_vm/src/builtins/string.rs +++ b/crates/oxide_builtins/src/string.rs @@ -2,19 +2,17 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; use memchr::memchr; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -fn this_string(vm: &Vm, args: &[u8]) -> String { - coercion::to_string(vm.reg(args[0])) +fn this_string(vm: &H, args: &[u8]) -> String { + oxide_runtime_api::to_string(vm.reg(args[0])) } -pub fn string_from_char_code(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_from_char_code(vm: &mut H, args: &[u8]) -> NativeResult { let mut out = String::new(); for &arg_reg in args.iter().skip(1) { - let code = coercion::to_uint32(vm.reg(arg_reg)) & 0xFFFF; + let code = oxide_runtime_api::to_uint32(vm.reg(arg_reg)) & 0xFFFF; if let Some(ch) = char::from_u32(code) { out.push(ch); } else { @@ -24,7 +22,7 @@ pub fn string_from_char_code(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&out)) } -pub fn string_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_value_of(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); if this_val.is_string() { return NativeResult::Ok(this_val); @@ -38,15 +36,15 @@ pub fn string_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult { } } } - NativeResult::Err(crate::builtins::error::create_type_error( + NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.valueOf called on non-String object", )) } -pub fn string_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let s = if args.len() > 1 { - coercion::to_string(vm.reg(args[1])) + oxide_runtime_api::to_string(vm.reg(args[1])) } else { String::new() }; @@ -76,7 +74,7 @@ pub fn string_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } -pub fn string_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); if this_val.is_string() { return NativeResult::Ok(this_val); @@ -90,13 +88,13 @@ pub fn string_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { } } } - NativeResult::Err(crate::builtins::error::create_type_error( + NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.toString called on non-String object", )) } -fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue { +fn make_string_array(vm: &mut H, parts: &[String]) -> JsValue { let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject; let n = parts.len(); let arr = @@ -112,8 +110,8 @@ fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue { JsValue::from_js_object(arr) } -fn as_string(_vm: &Vm, val: JsValue) -> String { - coercion::to_string(val) +fn as_string(_vm: &H, val: JsValue) -> String { + oxide_runtime_api::to_string(val) } fn char_len(s: &str) -> usize { @@ -137,7 +135,7 @@ fn take_chars(s: &str, count: usize) -> String { s.chars().take(count).collect() } -fn is_regexp_obj(val: JsValue, vm: &Vm) -> bool { +fn is_regexp_obj(val: JsValue, vm: &H) -> bool { if !val.is_object() { return false; } @@ -158,7 +156,7 @@ fn is_regexp_obj(val: JsValue, vm: &Vm) -> bool { std::ptr::eq(proto_ptr, rp) } -pub fn string_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_index_of(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -166,7 +164,7 @@ pub fn string_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { } let search = as_string(vm, vm.reg(args[1])); let pos = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as usize).min(n) + (oxide_runtime_api::to_number(vm.reg(args[2])) as usize).min(n) } else { 0 }; @@ -189,7 +187,7 @@ pub fn string_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } -pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_includes(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -197,7 +195,7 @@ pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult { } let search = as_string(vm, vm.reg(args[1])); let pos = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as usize).min(n) + (oxide_runtime_api::to_number(vm.reg(args[2])) as usize).min(n) } else { 0 }; @@ -214,7 +212,7 @@ pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult { } } -pub fn string_char_at(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_char_at(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { if s.is_empty() { @@ -222,7 +220,7 @@ pub fn string_char_at(vm: &mut Vm, args: &[u8]) -> NativeResult { } return NativeResult::Ok(vm.new_string(&take_chars(&s, 1))); } - let idx = coercion::to_number(vm.reg(args[1])) as i32; + let idx = oxide_runtime_api::to_number(vm.reg(args[1])) as i32; if idx < 0 || idx as usize >= char_len(&s) { return NativeResult::Ok(vm.new_string("")); } @@ -230,7 +228,7 @@ pub fn string_char_at(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&ch)) } -pub fn string_char_code_at(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_char_code_at(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { if s.is_empty() { @@ -238,26 +236,26 @@ pub fn string_char_code_at(vm: &mut Vm, args: &[u8]) -> NativeResult { } return NativeResult::Ok(JsValue::int(s.chars().next().unwrap() as i32)); } - let idx = coercion::to_number(vm.reg(args[1])) as i32; + let idx = oxide_runtime_api::to_number(vm.reg(args[1])) as i32; if idx < 0 || idx as usize >= char_len(&s) { return NativeResult::Ok(JsValue::float(f64::NAN)); } NativeResult::Ok(JsValue::int(s.chars().nth(idx as usize).unwrap() as i32)) } -pub fn string_concat(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_concat(vm: &mut H, args: &[u8]) -> NativeResult { let mut result = this_string(vm, args); for &arg_reg in args.iter().skip(1) { - result.push_str(&coercion::to_string(vm.reg(arg_reg))); + result.push_str(&oxide_runtime_api::to_string(vm.reg(arg_reg))); } NativeResult::Ok(vm.new_string(&result)) } -pub fn string_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_slice(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let n = char_len(&s) as i32; let start = if args.len() > 1 { - let v = coercion::to_number(vm.reg(args[1])) as i32; + let v = oxide_runtime_api::to_number(vm.reg(args[1])) as i32; if v < 0 { (n + v).max(0) } else { @@ -267,7 +265,7 @@ pub fn string_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { 0 }; let end = if args.len() > 2 { - let v = coercion::to_number(vm.reg(args[2])) as i32; + let v = oxide_runtime_api::to_number(vm.reg(args[2])) as i32; if v < 0 { (n + v).max(0) } else { @@ -282,16 +280,16 @@ pub fn string_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(result)) } -pub fn string_substring(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_substring(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let n = char_len(&s) as i32; let mut start = if args.len() > 1 { - (coercion::to_number(vm.reg(args[1])) as i32).max(0).min(n) + (oxide_runtime_api::to_number(vm.reg(args[1])) as i32).max(0).min(n) } else { 0 }; let mut end = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as i32).max(0).min(n) + (oxide_runtime_api::to_number(vm.reg(args[2])) as i32).max(0).min(n) } else { n }; @@ -302,41 +300,41 @@ pub fn string_substring(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(result)) } -pub fn string_to_upper_case(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_to_upper_case(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); NativeResult::Ok(vm.new_string(&s.to_uppercase())) } -pub fn string_to_lower_case(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_to_lower_case(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); NativeResult::Ok(vm.new_string(&s.to_lowercase())) } -pub fn string_trim(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_trim(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim())) } -pub fn string_repeat(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_repeat(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let n = if args.len() > 1 { - (coercion::to_number(vm.reg(args[1])) as usize).min(10000) + (oxide_runtime_api::to_number(vm.reg(args[1])) as usize).min(10000) } else { 1 }; NativeResult::Ok(vm.new_string(&s.repeat(n))) } -pub fn string_pad_start(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_pad_start(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let s_len = char_len(&s); let target = if args.len() > 1 { - coercion::to_number(vm.reg(args[1])) as usize + oxide_runtime_api::to_number(vm.reg(args[1])) as usize } else { s_len }; if target > 10000 { - return NativeResult::Err(crate::builtins::error::create_range_error(vm, "Invalid string length")); + return NativeResult::Err(crate::error::create_range_error(vm, "Invalid string length")); } let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() }; if s_len >= target || pad.is_empty() { @@ -350,16 +348,16 @@ pub fn string_pad_start(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&out)) } -pub fn string_pad_end(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_pad_end(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let s_len = char_len(&s); let target = if args.len() > 1 { - coercion::to_number(vm.reg(args[1])) as usize + oxide_runtime_api::to_number(vm.reg(args[1])) as usize } else { s_len }; if target > 10000 { - return NativeResult::Err(crate::builtins::error::create_range_error(vm, "Invalid string length")); + return NativeResult::Err(crate::error::create_range_error(vm, "Invalid string length")); } let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() }; if s_len >= target || pad.is_empty() { @@ -373,7 +371,7 @@ pub fn string_pad_end(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&out)) } -pub fn string_starts_with(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_starts_with(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -381,14 +379,14 @@ pub fn string_starts_with(vm: &mut Vm, args: &[u8]) -> NativeResult { } let search = as_string(vm, vm.reg(args[1])); let pos = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as usize).min(n) + (oxide_runtime_api::to_number(vm.reg(args[2])) as usize).min(n) } else { 0 }; NativeResult::Ok(JsValue::bool(s[byte_index_at_char(&s, pos)..].starts_with(&search))) } -pub fn string_ends_with(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_ends_with(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -396,14 +394,14 @@ pub fn string_ends_with(vm: &mut Vm, args: &[u8]) -> NativeResult { } let search = as_string(vm, vm.reg(args[1])); let end_pos = if args.len() > 2 { - (coercion::to_number(vm.reg(args[2])) as usize).min(n) + (oxide_runtime_api::to_number(vm.reg(args[2])) as usize).min(n) } else { n }; NativeResult::Ok(JsValue::bool(s[..byte_index_at_char(&s, end_pos)].ends_with(&search))) } -pub fn string_split(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_split(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { let parts = vec![s.clone()]; @@ -411,7 +409,7 @@ pub fn string_split(vm: &mut Vm, args: &[u8]) -> NativeResult { } let sep_val = vm.reg(args[1]); let limit = if args.len() > 2 { - coercion::to_number(vm.reg(args[2])) as usize + oxide_runtime_api::to_number(vm.reg(args[2])) as usize } else { usize::MAX }; @@ -442,7 +440,7 @@ pub fn string_split(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(make_string_array(vm, &parts)) } -pub fn string_replace(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(vm.new_string(&s)); @@ -468,7 +466,7 @@ pub fn string_replace(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&result)) } -pub fn string_match_fn(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_match_fn(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); @@ -495,7 +493,7 @@ pub fn string_match_fn(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn string_search(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_search(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(JsValue::int(-1)); @@ -524,19 +522,23 @@ pub fn string_search(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(-1)) } -pub fn string_trim_start(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_trim_start(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim_start())) } -pub fn string_trim_end(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_trim_end(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim_end())) } -pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_code_point_at(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); - let pos = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) as usize } else { 0 }; + let pos = if args.len() > 1 { + oxide_runtime_api::to_number(vm.reg(args[1])) as usize + } else { + 0 + }; let chars: Vec = s.chars().collect(); if pos >= chars.len() { return NativeResult::Ok(JsValue::undefined()); @@ -552,7 +554,7 @@ pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::int(c as i32)) } -pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_normalize(vm: &mut H, args: &[u8]) -> NativeResult { use unicode_normalization::UnicodeNormalization; let s = this_string(vm, args); let form = if args.len() > 1 { as_string(vm, vm.reg(args[1])) } else { "NFC".to_string() }; @@ -565,7 +567,7 @@ pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(vm.new_string(&result)) } -pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_match_all(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Err(JsValue::undefined()); @@ -586,7 +588,7 @@ pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn string_replace_all(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(vm.new_string(&s)); diff --git a/crates/oxide_builtins/src/stubs.rs b/crates/oxide_builtins/src/stubs.rs new file mode 100644 index 0000000..ccc267d --- /dev/null +++ b/crates/oxide_builtins/src/stubs.rs @@ -0,0 +1,31 @@ +use oxide_runtime_api::{NativeResult, VmHost}; + +fn stub_error(vm: &mut H, name: &str) -> NativeResult { + NativeResult::Err(crate::error::create_type_error(vm, &format!("{name} is not implemented"))) +} + +// Architecturally deferred — these features are excluded from the supported language subset by design. +pub fn proxy_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "Proxy") +} +pub fn bigint_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "BigInt") +} +pub fn weakmap_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "WeakMap") +} +pub fn weakset_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "WeakSet") +} +pub fn weakref_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "WeakRef") +} +pub fn finalization_registry_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "FinalizationRegistry") +} +pub fn shared_array_buffer_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "SharedArrayBuffer") +} +pub fn atomics_stub(vm: &mut H, _args: &[u8]) -> NativeResult { + stub_error(vm, "Atomics") +} diff --git a/crates/oxide_vm/src/builtins/symbol.rs b/crates/oxide_builtins/src/symbol.rs similarity index 55% rename from crates/oxide_vm/src/builtins/symbol.rs rename to crates/oxide_builtins/src/symbol.rs index 49c6b15..7501423 100644 --- a/crates/oxide_vm/src/builtins/symbol.rs +++ b/crates/oxide_builtins/src/symbol.rs @@ -1,11 +1,9 @@ use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{NativeResult, VmHost}; -pub fn symbol_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn symbol_constructor(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); if this_val.is_object() { let ptr = this_val.as_js_object_ptr(); @@ -17,10 +15,7 @@ pub fn symbol_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { if !proto_ptr.is_null() { let sp = vm.session().builtin_world().symbol_proto.as_ptr() as *mut JsObject; if std::ptr::eq(proto_ptr, sp) { - return NativeResult::Err(crate::builtins::error::create_type_error( - vm, - "Symbol is not a constructor", - )); + return NativeResult::Err(crate::error::create_type_error(vm, "Symbol is not a constructor")); } } } @@ -28,54 +23,51 @@ pub fn symbol_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { } let description = if args.len() > 1 { - coercion::to_string(vm.reg(args[1])) + oxide_runtime_api::to_string(vm.reg(args[1])) } else { String::new() }; - let idx = vm.symbols.intern(description); + let idx = vm.symbol_intern(description); NativeResult::Ok(JsValue::symbol(idx)) } -pub fn symbol_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn symbol_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(args[0]); if !this_val.is_symbol() { - return NativeResult::Err(crate::builtins::error::create_type_error( - vm, - "Symbol.prototype.toString requires a Symbol", - )); + return NativeResult::Err(crate::error::create_type_error(vm, "Symbol.prototype.toString requires a Symbol")); } let idx = this_val.as_symbol_index(); - let desc = vm.symbols.description(idx).unwrap_or("").to_string(); + let desc = vm.symbol_description(idx).unwrap_or("").to_string(); let result = format!("Symbol({})", desc); NativeResult::Ok(vm.new_string(&result)) } -pub fn symbol_for(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn symbol_for(vm: &mut H, args: &[u8]) -> NativeResult { let key = if args.len() > 1 { - coercion::to_string(vm.reg(args[1])) + oxide_runtime_api::to_string(vm.reg(args[1])) } else { "undefined".to_string() }; - if let Some(idx) = vm.symbols.lookup_global(&key) { + if let Some(idx) = vm.symbol_lookup_global(&key) { return NativeResult::Ok(JsValue::symbol(idx)); } - let idx = vm.symbols.intern(key.clone()); - vm.symbols.register_global(key, idx); + let idx = vm.symbol_intern(key.clone()); + vm.symbol_register_global(key, idx); NativeResult::Ok(JsValue::symbol(idx)) } -pub fn symbol_key_for(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn symbol_key_for(vm: &mut H, args: &[u8]) -> NativeResult { let sym = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; if !sym.is_symbol() { - return NativeResult::Err(crate::builtins::error::create_type_error(vm, "is not a symbol")); + return NativeResult::Err(crate::error::create_type_error(vm, "is not a symbol")); } let idx = sym.as_symbol_index(); - match vm.symbols.key_for_id(idx) { + match vm.symbol_key_for_id(idx) { Some(key) => NativeResult::Ok(vm.new_string(&key)), None => NativeResult::Ok(JsValue::undefined()), } diff --git a/crates/oxide_vm/src/builtins/typed_array.rs b/crates/oxide_builtins/src/typed_array.rs similarity index 88% rename from crates/oxide_vm/src/builtins/typed_array.rs rename to crates/oxide_builtins/src/typed_array.rs index 26d8779..bfc80da 100644 --- a/crates/oxide_vm/src/builtins/typed_array.rs +++ b/crates/oxide_builtins/src/typed_array.rs @@ -2,10 +2,9 @@ use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes, TypedArrayKind}; use oxide_types::value::JsValue; -use crate::builtins::array_buffer::{array_buffer_data_ptr, new_array_buffer, MAX_ARRAY_BUFFER_LENGTH}; -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; +use crate::array_buffer::{array_buffer_data_ptr, new_array_buffer, MAX_ARRAY_BUFFER_LENGTH}; + +use oxide_runtime_api::{NativeResult, VmHost}; #[derive(Clone, Copy)] pub(crate) struct TypedArrayData { @@ -24,16 +23,16 @@ macro_rules! native_try { }; } -fn type_error(vm: &mut Vm, msg: &str) -> JsValue { - crate::builtins::error::create_type_error(vm, msg) +fn type_error(vm: &mut H, msg: &str) -> JsValue { + crate::error::create_type_error(vm, msg) } -fn range_error(vm: &mut Vm, msg: &str) -> JsValue { - crate::builtins::error::create_range_error(vm, msg) +fn range_error(vm: &mut H, msg: &str) -> JsValue { + crate::error::create_range_error(vm, msg) } -fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result { - let n = coercion::to_number(value); +fn to_index(vm: &mut H, value: JsValue, msg: &str) -> Result { + let n = oxide_runtime_api::to_number(value); if n.is_nan() { return Ok(0); } @@ -43,8 +42,8 @@ fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result { Ok(n.trunc() as usize) } -fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize { - let n = coercion::to_number(value); +fn normalize_index(_vm: &mut H, value: JsValue, len: usize) -> usize { + let n = oxide_runtime_api::to_number(value); if n.is_nan() { return 0; } @@ -56,12 +55,12 @@ fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize { } } -fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) { +fn set_named_prop(vm: &mut H, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) { let si = vm.kernel_core().perm_interner().intern(name).0; let _ = vm.define_data_property(obj, si, value, attributes); } -fn typed_array_proto_ptr(vm: &mut Vm, kind: TypedArrayKind) -> *mut JsObject { +fn typed_array_proto_ptr(vm: &mut H, kind: TypedArrayKind) -> *mut JsObject { let world = vm.session().builtin_world(); match kind { TypedArrayKind::Int8 => world.int8array_proto.as_ptr() as *mut JsObject, @@ -78,8 +77,8 @@ fn typed_array_proto_ptr(vm: &mut Vm, kind: TypedArrayKind) -> *mut JsObject { } } -fn create_typed_array( - vm: &mut Vm, kind: TypedArrayKind, buffer: JsValue, byte_offset: usize, length: usize, +fn create_typed_array( + vm: &mut H, kind: TypedArrayKind, buffer: JsValue, byte_offset: usize, length: usize, ) -> *mut JsObject { let proto = typed_array_proto_ptr(vm, kind); let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto)); @@ -127,7 +126,7 @@ fn typed_array_data_ptr(obj: &JsObject) -> Option<*mut TypedArrayData> { obj.native_fn().map(|ptr| ptr.as_ptr() as *mut TypedArrayData) } -pub(crate) fn typed_array_native_edges(obj: &JsObject) -> Vec { +pub fn typed_array_native_edges(obj: &JsObject) -> Vec { let Some(ptr) = typed_array_data_ptr(obj) else { return Vec::new(); }; @@ -142,7 +141,7 @@ pub(crate) fn typed_array_native_edges(obj: &JsObject) -> Vec { } } -pub(crate) fn clone_typed_array_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) +pub fn clone_typed_array_native_with_rewrite(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -158,7 +157,7 @@ where new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) })); } -pub(crate) fn rewrite_typed_array_native(obj: &mut JsObject, mut rewrite: F) +pub fn rewrite_typed_array_native(obj: &mut JsObject, mut rewrite: F) where F: FnMut(JsValue) -> JsValue, { @@ -173,7 +172,7 @@ where } } -pub(crate) fn drop_typed_array_native(obj: &mut JsObject) -> u64 { +pub fn drop_typed_array_native(obj: &mut JsObject) -> u64 { let Some(ptr) = typed_array_data_ptr(obj) else { return 0; }; @@ -185,7 +184,7 @@ pub(crate) fn drop_typed_array_native(obj: &mut JsObject) -> u64 { std::mem::size_of::() as u64 } -pub(crate) fn get_typed_array_data(vm: &mut Vm, this_val: JsValue) -> Result { +pub(crate) fn get_typed_array_data(vm: &mut H, this_val: JsValue) -> Result { if !this_val.is_object() { return Err(type_error(vm, "TypedArray method called on incompatible receiver")); } @@ -236,8 +235,8 @@ fn read_element(kind: TypedArrayKind, bytes: &[u8], offset: usize) -> JsValue { } } -fn numeric_value(_vm: &mut Vm, value: JsValue) -> f64 { - coercion::to_number(value) +fn numeric_value(_vm: &mut H, value: JsValue) -> f64 { + oxide_runtime_api::to_number(value) } fn write_element(kind: TypedArrayKind, bytes: &mut [u8], offset: usize, value: f64) { @@ -256,7 +255,7 @@ fn write_element(kind: TypedArrayKind, bytes: &mut [u8], offset: usize, value: f } } -fn collect_array_like(vm: &mut Vm, value: JsValue) -> Result, JsValue> { +fn collect_array_like(vm: &mut H, value: JsValue) -> Result, JsValue> { if !value.is_object() { return Err(type_error(vm, "TypedArray source must be array-like")); } @@ -280,7 +279,7 @@ fn collect_array_like(vm: &mut Vm, value: JsValue) -> Result, JsVal Err(type_error(vm, "TypedArray source must be Array, ArrayBuffer, or TypedArray")) } -fn typed_array_new(vm: &mut Vm, args: &[u8], kind: TypedArrayKind) -> NativeResult { +fn typed_array_new(vm: &mut H, args: &[u8], kind: TypedArrayKind) -> NativeResult { let first = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::int(0) }; let bpe = kind.bytes_per_element(); @@ -342,7 +341,7 @@ fn typed_array_new(vm: &mut Vm, args: &[u8], kind: TypedArrayKind) -> NativeResu macro_rules! typed_array_ctor { ($name:ident, $kind:ident) => { - pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult { + pub fn $name(vm: &mut H, args: &[u8]) -> NativeResult { typed_array_new(vm, args, TypedArrayKind::$kind) } }; @@ -360,7 +359,7 @@ typed_array_ctor!(float64array_constructor, Float64); typed_array_ctor!(bigint64array_constructor, BigInt64); typed_array_ctor!(biguint64array_constructor, BigUint64); -pub fn typed_array_at(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn typed_array_at(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); let raw_index = if args.len() > 1 { @@ -377,7 +376,7 @@ pub fn typed_array_at(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(read_element(view.kind, buffer, absolute_byte_offset(view, idx as usize))) } -pub fn typed_array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn typed_array_fill(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() }; @@ -400,7 +399,7 @@ pub fn typed_array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(this_val) } -pub fn typed_array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn typed_array_slice(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); let start = if args.len() > 1 { @@ -425,7 +424,7 @@ pub fn typed_array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::from_js_object(create_typed_array(vm, view.kind, buffer, 0, count))) } -pub fn typed_array_subarray(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn typed_array_subarray(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); let start = if args.len() > 1 { @@ -449,7 +448,7 @@ pub fn typed_array_subarray(vm: &mut Vm, args: &[u8]) -> NativeResult { ))) } -pub fn typed_array_set(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn typed_array_set(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); let view = native_try!(get_typed_array_data(vm, this_val)); if args.len() < 2 { @@ -474,7 +473,7 @@ pub fn typed_array_set(vm: &mut Vm, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::undefined()) } -pub fn typed_array_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { +pub fn typed_array_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); native_try!(get_typed_array_data(vm, this_val)); NativeResult::Ok(vm.new_string("[object TypedArray]")) diff --git a/crates/oxide_runtime_api/Cargo.toml b/crates/oxide_runtime_api/Cargo.toml index 8d10ca3..cd1ce93 100644 --- a/crates/oxide_runtime_api/Cargo.toml +++ b/crates/oxide_runtime_api/Cargo.toml @@ -9,3 +9,4 @@ description = "OxideJS - VmHost trait: the abstract Vm interface builtins are wr [dependencies] oxide_types = { path = "../oxide_types" } oxide_kernel = { path = "../oxide_kernel" } +ryu.workspace = true diff --git a/crates/oxide_runtime_api/src/lib.rs b/crates/oxide_runtime_api/src/lib.rs index 26fe834..1f26cdc 100644 --- a/crates/oxide_runtime_api/src/lib.rs +++ b/crates/oxide_runtime_api/src/lib.rs @@ -14,8 +14,9 @@ use std::sync::Arc; use oxide_kernel::kernel::{KernelCore, KernelSession}; -use oxide_types::mem::Epoch; +use oxide_types::mem::{Epoch, P}; use oxide_types::object::{JsObject, PropAttributes}; +use oxide_types::shape::EMPTY_SHAPE_ID; use oxide_types::value::JsValue; /// Return value of every builtin native function. @@ -105,4 +106,345 @@ pub trait VmHost { fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result, String>; fn raise_type_error(&mut self, msg: &str) -> Result<(), String>; fn error_message_text(&self, kind: &str, msg: &str) -> String; + fn call_stack_function_names(&self) -> Vec; + fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue; + fn step_rng(&mut self); + fn math_rng_value(&self) -> f64; + fn sub_module_function_name(&self, sub_idx: u16) -> String; + fn symbol_intern(&mut self, desc: String) -> u32; + fn symbol_description(&self, idx: u32) -> Option<&str>; + fn symbol_lookup_global(&self, key: &str) -> Option; + fn symbol_register_global(&mut self, key: String, idx: u32); + fn symbol_key_for_id(&self, idx: u32) -> Option; +} + +pub fn format_error_message(kind: &str, msg: &str) -> String { + if msg.is_empty() || msg == kind || msg.starts_with(&format!("{kind}:")) { + msg.to_string() + } else { + format!("{kind}: {msg}") + } +} + +/// Borrow a string value's content. +/// +/// # Safety +/// `val` must be a string `JsValue` whose `JsString` pointer is alive. +#[inline] +pub unsafe fn string_data(val: JsValue) -> &'static str { + (*val.as_string_ptr()).as_str() +} + +#[inline] +pub fn string_value_eq(a: JsValue, b: JsValue) -> bool { + if a.as_string_ptr() == b.as_string_ptr() { + return true; + } + let sa = unsafe { &*a.as_string_ptr() }; + let sb = unsafe { &*b.as_string_ptr() }; + sa.hash == sb.hash && sa.data == sb.data +} + +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_string() { + let s = unsafe { string_data(val) }; + return s.parse::().unwrap_or(f64::NAN); + } + if val.is_object() { + return f64::NAN; + } + f64::NAN +} + +pub fn to_uint32(val: JsValue) -> u32 { + let n = to_number(val); + if n == 0.0 || !n.is_finite() { + return 0; + } + n.trunc().rem_euclid(4_294_967_296.0) as u32 +} + +pub fn to_int32(val: JsValue) -> i32 { + let int = to_uint32(val); + if int > i32::MAX as u32 { + (int as i64 - 4_294_967_296i64) as i32 + } else { + int as i32 + } +} + +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() + }; + } + if d.is_finite() && d.fract() == 0.0 { + return (d as i64).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_string() { + return unsafe { string_data(val) }.to_string(); + } + if val.is_object() { + return "[object]".to_string(); + } + 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_string() { + return !unsafe { (*val.as_string_ptr()).is_empty() }; + } + if val.is_object() { + return true; + } + if val.is_symbol() { + 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_string() && rhs.is_string() { + return string_value_eq(lhs, rhs); + } + 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() { + return lhs.is_object() && rhs.is_object() && lhs.as_ptr() == rhs.as_ptr(); + } + 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_string() && rhs.is_string() { + return string_value_eq(lhs, rhs); + } + if lhs.is_object() && rhs.is_object() { + return lhs.as_ptr() == rhs.as_ptr(); + } + if lhs.is_symbol() && rhs.is_symbol() { + return lhs.as_symbol_index() == rhs.as_symbol_index(); + } + 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 { + if lhs.is_string() && rhs.is_string() { + let ls = unsafe { string_data(lhs) }; + let rs = unsafe { string_data(rhs) }; + return Some(ls < rs); + } + 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 to_f64(val: JsValue) -> f64 { + if val.is_int() { + val.as_int() as f64 + } else if val.is_double() { + val.as_double() + } else { + f64::NAN + } +} + +pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool { + if lhs.is_double() && rhs.is_double() { + let a = lhs.as_double(); + let b = rhs.as_double(); + if a.is_nan() && b.is_nan() { + return true; + } + if a == 0.0 && b == 0.0 { + let a_neg = a.is_sign_negative(); + let b_neg = b.is_sign_negative(); + return a_neg == b_neg; + } + return a == b; + } + if lhs.is_int() && rhs.is_int() { + return lhs.as_int() == rhs.as_int(); + } + if (lhs.is_int() || lhs.is_double()) && (rhs.is_int() || rhs.is_double()) { + let a = to_f64(lhs); + let b = to_f64(rhs); + if a.is_nan() && b.is_nan() { + return true; + } + if a == 0.0 && b == 0.0 { + return a.is_sign_negative() == b.is_sign_negative(); + } + return a == b; + } + 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_string() && rhs.is_string() { + return string_value_eq(lhs, rhs); + } + if lhs.is_object() && rhs.is_object() { + return lhs.as_ptr() == rhs.as_ptr(); + } + if lhs.is_symbol() && rhs.is_symbol() { + return lhs.as_symbol_index() == rhs.as_symbol_index(); + } + false +} + +pub fn strict_equality(lhs: JsValue, rhs: JsValue) -> bool { + if lhs.is_double() && rhs.is_double() { + return strict_double_eq(lhs.as_double(), rhs.as_double()); + } + same_value(lhs, rhs) +} + +pub fn to_object(val: JsValue, host: &mut H) -> Result { + if val.is_object() { + return Ok(val); + } + if val.is_null() || val.is_undefined() { + return Err(host.error_message_text("TypeError", "Cannot convert null or undefined to object")); + } + let world = host.session().builtin_world(); + let (proto_ptr, type_tag) = if val.is_string() { + (P::as_ptr(&world.string_proto) as *mut JsObject, JsObject::OBJ_TYPE_STRING_OBJ) + } else if val.is_int() || val.is_double() { + (P::as_ptr(&world.number_proto) as *mut JsObject, JsObject::OBJ_TYPE_NUMBER_OBJ) + } else if val.is_bool() { + (P::as_ptr(&world.boolean_proto) as *mut JsObject, JsObject::OBJ_TYPE_BOOLEAN_OBJ) + } else { + (P::as_ptr(&world.object_proto) as *mut JsObject, JsObject::OBJ_TYPE_PLAIN) + }; + let proto_val = JsValue::from_js_object(proto_ptr); + let obj = host.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); + let obj_val = JsValue::from_js_object(obj); + let obj_ref = unsafe { &mut *obj }; + obj_ref.type_tag = type_tag; + obj_ref.ensure_hash_props().push(val); + obj_ref.set_prop_count(1); + Ok(obj_val) } diff --git a/crates/oxide_vm/Cargo.toml b/crates/oxide_vm/Cargo.toml index 700b1e4..7c86643 100644 --- a/crates/oxide_vm/Cargo.toml +++ b/crates/oxide_vm/Cargo.toml @@ -10,6 +10,7 @@ description = "OxideJS - Register-based VM with epoch arena memory" oxide_bytecode = { path = "../oxide_bytecode" } oxide_kernel = { path = "../oxide_kernel" } oxide_runtime_api = { path = "../oxide_runtime_api" } +oxide_builtins = { path = "../oxide_builtins" } oxide_types = { path = "../oxide_types" } bumpalo.workspace = true fast-float.workspace = true diff --git a/crates/oxide_vm/src/bindings/bind_array.rs b/crates/oxide_vm/src/bindings/bind_array.rs index 403a6cb..3063e68 100644 --- a/crates/oxide_vm/src/bindings/bind_array.rs +++ b/crates/oxide_vm/src/bindings/bind_array.rs @@ -8,36 +8,36 @@ use crate::bind_constructor; pub fn bind_array(core: &Arc, session: &KernelSession, global: &mut JsObject) { let _array_methods = ArrayMethods { - is_array: crate::builtins::array::array_is_array as *const (), - push: crate::builtins::array::array_push as *const (), - pop: crate::builtins::array::array_pop as *const (), - slice: crate::builtins::array::array_slice as *const (), - splice: crate::builtins::array::array_splice as *const (), - concat: crate::builtins::array::array_concat as *const (), - join: crate::builtins::array::array_join as *const (), - index_of: crate::builtins::array::array_index_of as *const (), - includes: crate::builtins::array::array_includes as *const (), - reverse: crate::builtins::array::array_reverse as *const (), - for_each: crate::builtins::array::array_for_each as *const (), - map: crate::builtins::array::array_map as *const (), - filter: crate::builtins::array::array_filter as *const (), - reduce: crate::builtins::array::array_reduce as *const (), - find: crate::builtins::array::array_find as *const (), - some: crate::builtins::array::array_some as *const (), - every: crate::builtins::array::array_every as *const (), - flat: crate::builtins::array::array_flat as *const (), - flat_map: crate::builtins::array::array_flat_map as *const (), - shift: crate::builtins::array::array_shift as *const (), - unshift: crate::builtins::array::array_unshift as *const (), - fill: crate::builtins::array::array_fill as *const (), - copy_within: crate::builtins::array::array_copy_within as *const (), - at: crate::builtins::array::array_at as *const (), - last_index_of: crate::builtins::array::array_last_index_of as *const (), - find_index: crate::builtins::array::array_find_index as *const (), - find_last: crate::builtins::array::array_find_last as *const (), - reduce_right: crate::builtins::array::array_reduce_right as *const (), - sort: crate::builtins::array::array_sort as *const (), - values: crate::builtins::array::array_values as *const (), + is_array: oxide_builtins::array::array_is_array:: as *const (), + push: oxide_builtins::array::array_push:: as *const (), + pop: oxide_builtins::array::array_pop:: as *const (), + slice: oxide_builtins::array::array_slice:: as *const (), + splice: oxide_builtins::array::array_splice:: as *const (), + concat: oxide_builtins::array::array_concat:: as *const (), + join: oxide_builtins::array::array_join:: as *const (), + index_of: oxide_builtins::array::array_index_of:: as *const (), + includes: oxide_builtins::array::array_includes:: as *const (), + reverse: oxide_builtins::array::array_reverse:: as *const (), + for_each: oxide_builtins::array::array_for_each:: as *const (), + map: oxide_builtins::array::array_map:: as *const (), + filter: oxide_builtins::array::array_filter:: as *const (), + reduce: oxide_builtins::array::array_reduce:: as *const (), + find: oxide_builtins::array::array_find:: as *const (), + some: oxide_builtins::array::array_some:: as *const (), + every: oxide_builtins::array::array_every:: as *const (), + flat: oxide_builtins::array::array_flat:: as *const (), + flat_map: oxide_builtins::array::array_flat_map:: as *const (), + shift: oxide_builtins::array::array_shift:: as *const (), + unshift: oxide_builtins::array::array_unshift:: as *const (), + fill: oxide_builtins::array::array_fill:: as *const (), + copy_within: oxide_builtins::array::array_copy_within:: as *const (), + at: oxide_builtins::array::array_at:: as *const (), + last_index_of: oxide_builtins::array::array_last_index_of:: as *const (), + find_index: oxide_builtins::array::array_find_index:: as *const (), + find_last: oxide_builtins::array::array_find_last:: as *const (), + reduce_right: oxide_builtins::array::array_reduce_right:: as *const (), + sort: oxide_builtins::array::array_sort:: as *const (), + values: oxide_builtins::array::array_values:: as *const (), }; session.builtin_world().bind_array_methods( @@ -47,5 +47,5 @@ pub fn bind_array(core: &Arc, session: &KernelSession, global: &mut ); let ctor_ptr = session.builtin_world().array_constructor.as_ptr() as *mut JsObject; - bind_constructor!(core, global, "Array", ctor_ptr, crate::builtins::array::array_constructor, 1, hash: true); + bind_constructor!(core, global, "Array", ctor_ptr, oxide_builtins::array::array_constructor::, 1, hash: true); } diff --git a/crates/oxide_vm/src/bindings/bind_array_buffer.rs b/crates/oxide_vm/src/bindings/bind_array_buffer.rs index b3cd284..eb52e47 100644 --- a/crates/oxide_vm/src/bindings/bind_array_buffer.rs +++ b/crates/oxide_vm/src/bindings/bind_array_buffer.rs @@ -11,13 +11,21 @@ pub fn bind_array_buffer(core: &Arc, session: &KernelSession, global let proto_ptr = session.builtin_world().array_buffer_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - configure_native_constructor(ctor, crate::builtins::array_buffer::array_buffer_constructor as *const (), 1); + configure_native_constructor( + ctor, + oxide_builtins::array_buffer::array_buffer_constructor:: as *const (), + 1, + ); apply_binding_table( session.builtin_world(), ctor, core, - &[("isView", crate::builtins::array_buffer::array_buffer_is_view as *const (), 1)], + &[( + "isView", + oxide_builtins::array_buffer::array_buffer_is_view:: as *const (), + 1, + )], ); apply_binding_table( @@ -25,9 +33,17 @@ pub fn bind_array_buffer(core: &Arc, session: &KernelSession, global proto, core, &[ - ("byteLength", crate::builtins::array_buffer::array_buffer_byte_length as *const (), 0), - ("slice", crate::builtins::array_buffer::array_buffer_slice as *const (), 2), - ("toString", crate::builtins::array_buffer::array_buffer_to_string as *const (), 0), + ( + "byteLength", + oxide_builtins::array_buffer::array_buffer_byte_length:: as *const (), + 0, + ), + ("slice", oxide_builtins::array_buffer::array_buffer_slice:: as *const (), 2), + ( + "toString", + oxide_builtins::array_buffer::array_buffer_to_string:: as *const (), + 0, + ), ], ); @@ -36,7 +52,7 @@ pub fn bind_array_buffer(core: &Arc, session: &KernelSession, global global, "ArrayBuffer", ctor_ptr, - crate::builtins::array_buffer::array_buffer_constructor, + oxide_builtins::array_buffer::array_buffer_constructor::, 1, hash: true ); diff --git a/crates/oxide_vm/src/bindings/bind_boolean.rs b/crates/oxide_vm/src/bindings/bind_boolean.rs index 5106feb..f2e0218 100644 --- a/crates/oxide_vm/src/bindings/bind_boolean.rs +++ b/crates/oxide_vm/src/bindings/bind_boolean.rs @@ -11,17 +11,25 @@ pub fn bind_boolean(core: &Arc, session: &KernelSession, global: &mu let proto_ptr = session.builtin_world().boolean_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - configure_native_constructor(ctor, crate::builtins::boolean::boolean_constructor as *const (), 1); + configure_native_constructor(ctor, oxide_builtins::boolean::boolean_constructor:: as *const (), 1); apply_binding_table( session.builtin_world(), proto, core, &[ - ("valueOf", crate::builtins::boolean::boolean_prototype_value_of as *const (), 0), - ("toString", crate::builtins::boolean::boolean_prototype_to_string as *const (), 0), + ( + "valueOf", + oxide_builtins::boolean::boolean_prototype_value_of:: as *const (), + 0, + ), + ( + "toString", + oxide_builtins::boolean::boolean_prototype_to_string:: as *const (), + 0, + ), ], ); - bind_constructor!(core, global, "Boolean", ctor_ptr, crate::builtins::boolean::boolean_constructor, 1, hash: true); + bind_constructor!(core, global, "Boolean", ctor_ptr, oxide_builtins::boolean::boolean_constructor::, 1, hash: true); } diff --git a/crates/oxide_vm/src/bindings/bind_data_view.rs b/crates/oxide_vm/src/bindings/bind_data_view.rs index 7661394..0710d07 100644 --- a/crates/oxide_vm/src/bindings/bind_data_view.rs +++ b/crates/oxide_vm/src/bindings/bind_data_view.rs @@ -11,36 +11,116 @@ pub fn bind_data_view(core: &Arc, session: &KernelSession, global: & let proto_ptr = session.builtin_world().data_view_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - configure_native_constructor(ctor, crate::builtins::data_view::data_view_constructor as *const (), 3); + configure_native_constructor( + ctor, + oxide_builtins::data_view::data_view_constructor:: as *const (), + 3, + ); apply_binding_table( session.builtin_world(), proto, core, &[ - ("getInt8", crate::builtins::data_view::data_view_get_int8 as *const (), 1), - ("getUint8", crate::builtins::data_view::data_view_get_uint8 as *const (), 1), - ("getInt16", crate::builtins::data_view::data_view_get_int16 as *const (), 2), - ("getUint16", crate::builtins::data_view::data_view_get_uint16 as *const (), 2), - ("getInt32", crate::builtins::data_view::data_view_get_int32 as *const (), 2), - ("getUint32", crate::builtins::data_view::data_view_get_uint32 as *const (), 2), - ("getFloat32", crate::builtins::data_view::data_view_get_float32 as *const (), 2), - ("getFloat64", crate::builtins::data_view::data_view_get_float64 as *const (), 2), - ("getBigInt64", crate::builtins::data_view::data_view_get_big_int64 as *const (), 2), - ("getBigUint64", crate::builtins::data_view::data_view_get_big_uint64 as *const (), 2), - ("setInt8", crate::builtins::data_view::data_view_set_int8 as *const (), 2), - ("setUint8", crate::builtins::data_view::data_view_set_uint8 as *const (), 2), - ("setInt16", crate::builtins::data_view::data_view_set_int16 as *const (), 3), - ("setUint16", crate::builtins::data_view::data_view_set_uint16 as *const (), 3), - ("setInt32", crate::builtins::data_view::data_view_set_int32 as *const (), 3), - ("setUint32", crate::builtins::data_view::data_view_set_uint32 as *const (), 3), - ("setFloat32", crate::builtins::data_view::data_view_set_float32 as *const (), 3), - ("setFloat64", crate::builtins::data_view::data_view_set_float64 as *const (), 3), - ("setBigInt64", crate::builtins::data_view::data_view_set_big_int64 as *const (), 3), - ("setBigUint64", crate::builtins::data_view::data_view_set_big_uint64 as *const (), 3), - ("toString", crate::builtins::data_view::data_view_to_string as *const (), 0), + ("getInt8", oxide_builtins::data_view::data_view_get_int8:: as *const (), 1), + ( + "getUint8", + oxide_builtins::data_view::data_view_get_uint8:: as *const (), + 1, + ), + ( + "getInt16", + oxide_builtins::data_view::data_view_get_int16:: as *const (), + 2, + ), + ( + "getUint16", + oxide_builtins::data_view::data_view_get_uint16:: as *const (), + 2, + ), + ( + "getInt32", + oxide_builtins::data_view::data_view_get_int32:: as *const (), + 2, + ), + ( + "getUint32", + oxide_builtins::data_view::data_view_get_uint32:: as *const (), + 2, + ), + ( + "getFloat32", + oxide_builtins::data_view::data_view_get_float32:: as *const (), + 2, + ), + ( + "getFloat64", + oxide_builtins::data_view::data_view_get_float64:: as *const (), + 2, + ), + ( + "getBigInt64", + oxide_builtins::data_view::data_view_get_big_int64:: as *const (), + 2, + ), + ( + "getBigUint64", + oxide_builtins::data_view::data_view_get_big_uint64:: as *const (), + 2, + ), + ("setInt8", oxide_builtins::data_view::data_view_set_int8:: as *const (), 2), + ( + "setUint8", + oxide_builtins::data_view::data_view_set_uint8:: as *const (), + 2, + ), + ( + "setInt16", + oxide_builtins::data_view::data_view_set_int16:: as *const (), + 3, + ), + ( + "setUint16", + oxide_builtins::data_view::data_view_set_uint16:: as *const (), + 3, + ), + ( + "setInt32", + oxide_builtins::data_view::data_view_set_int32:: as *const (), + 3, + ), + ( + "setUint32", + oxide_builtins::data_view::data_view_set_uint32:: as *const (), + 3, + ), + ( + "setFloat32", + oxide_builtins::data_view::data_view_set_float32:: as *const (), + 3, + ), + ( + "setFloat64", + oxide_builtins::data_view::data_view_set_float64:: as *const (), + 3, + ), + ( + "setBigInt64", + oxide_builtins::data_view::data_view_set_big_int64:: as *const (), + 3, + ), + ( + "setBigUint64", + oxide_builtins::data_view::data_view_set_big_uint64:: as *const (), + 3, + ), + ( + "toString", + oxide_builtins::data_view::data_view_to_string:: as *const (), + 0, + ), ], ); - bind_constructor!(core, global, "DataView", ctor_ptr, crate::builtins::data_view::data_view_constructor, 3, hash: true); + bind_constructor!(core, global, "DataView", ctor_ptr, oxide_builtins::data_view::data_view_constructor::, 3, hash: true); } diff --git a/crates/oxide_vm/src/bindings/bind_date.rs b/crates/oxide_vm/src/bindings/bind_date.rs index e09bcbb..5b1d732 100644 --- a/crates/oxide_vm/src/bindings/bind_date.rs +++ b/crates/oxide_vm/src/bindings/bind_date.rs @@ -11,16 +11,16 @@ pub fn bind_date(core: &Arc, session: &KernelSession, global: &mut J let proto_ptr = session.builtin_world().date_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - configure_native_constructor(ctor, crate::builtins::date::date_constructor as *const (), 7); + configure_native_constructor(ctor, oxide_builtins::date::date_constructor:: as *const (), 7); apply_binding_table( session.builtin_world(), ctor, core, &[ - ("now", crate::builtins::date::date_now as *const (), 0), - ("parse", crate::builtins::date::date_parse as *const (), 1), - ("UTC", crate::builtins::date::date_utc as *const (), 7), + ("now", oxide_builtins::date::date_now:: as *const (), 0), + ("parse", oxide_builtins::date::date_parse:: as *const (), 1), + ("UTC", oxide_builtins::date::date_utc:: as *const (), 7), ], ); @@ -29,39 +29,67 @@ pub fn bind_date(core: &Arc, session: &KernelSession, global: &mut J proto, core, &[ - ("getTime", crate::builtins::date::date_get_time as *const (), 0), - ("getFullYear", crate::builtins::date::date_get_full_year as *const (), 0), - ("getMonth", crate::builtins::date::date_get_month as *const (), 0), - ("getDate", crate::builtins::date::date_get_date as *const (), 0), - ("getDay", crate::builtins::date::date_get_day as *const (), 0), - ("getHours", crate::builtins::date::date_get_hours as *const (), 0), - ("getMinutes", crate::builtins::date::date_get_minutes as *const (), 0), - ("getSeconds", crate::builtins::date::date_get_seconds as *const (), 0), - ("getMilliseconds", crate::builtins::date::date_get_milliseconds as *const (), 0), - ("getUTCFullYear", crate::builtins::date::date_get_utc_full_year as *const (), 0), - ("getUTCMonth", crate::builtins::date::date_get_utc_month as *const (), 0), - ("getUTCDate", crate::builtins::date::date_get_utc_date as *const (), 0), - ("getUTCDay", crate::builtins::date::date_get_utc_day as *const (), 0), - ("getUTCHours", crate::builtins::date::date_get_utc_hours as *const (), 0), - ("getUTCMinutes", crate::builtins::date::date_get_utc_minutes as *const (), 0), - ("getUTCSeconds", crate::builtins::date::date_get_utc_seconds as *const (), 0), - ("getUTCMilliseconds", crate::builtins::date::date_get_utc_milliseconds as *const (), 0), - ("getTimezoneOffset", crate::builtins::date::date_get_timezone_offset as *const (), 0), - ("setTime", crate::builtins::date::date_set_time as *const (), 1), - ("setFullYear", crate::builtins::date::date_set_full_year as *const (), 3), - ("setMonth", crate::builtins::date::date_set_month as *const (), 2), - ("setDate", crate::builtins::date::date_set_date as *const (), 1), - ("setHours", crate::builtins::date::date_set_hours as *const (), 4), - ("setMinutes", crate::builtins::date::date_set_minutes as *const (), 3), - ("setSeconds", crate::builtins::date::date_set_seconds as *const (), 2), - ("setMilliseconds", crate::builtins::date::date_set_milliseconds as *const (), 1), - ("toISOString", crate::builtins::date::date_to_iso_string as *const (), 0), - ("toJSON", crate::builtins::date::date_to_json as *const (), 1), - ("toString", crate::builtins::date::date_to_string as *const (), 0), - ("toDateString", crate::builtins::date::date_to_date_string as *const (), 0), - ("toTimeString", crate::builtins::date::date_to_time_string as *const (), 0), - ("toUTCString", crate::builtins::date::date_to_utc_string as *const (), 0), - ("valueOf", crate::builtins::date::date_value_of as *const (), 0), + ("getTime", oxide_builtins::date::date_get_time:: as *const (), 0), + ("getFullYear", oxide_builtins::date::date_get_full_year:: as *const (), 0), + ("getMonth", oxide_builtins::date::date_get_month:: as *const (), 0), + ("getDate", oxide_builtins::date::date_get_date:: as *const (), 0), + ("getDay", oxide_builtins::date::date_get_day:: as *const (), 0), + ("getHours", oxide_builtins::date::date_get_hours:: as *const (), 0), + ("getMinutes", oxide_builtins::date::date_get_minutes:: as *const (), 0), + ("getSeconds", oxide_builtins::date::date_get_seconds:: as *const (), 0), + ( + "getMilliseconds", + oxide_builtins::date::date_get_milliseconds:: as *const (), + 0, + ), + ( + "getUTCFullYear", + oxide_builtins::date::date_get_utc_full_year:: as *const (), + 0, + ), + ("getUTCMonth", oxide_builtins::date::date_get_utc_month:: as *const (), 0), + ("getUTCDate", oxide_builtins::date::date_get_utc_date:: as *const (), 0), + ("getUTCDay", oxide_builtins::date::date_get_utc_day:: as *const (), 0), + ("getUTCHours", oxide_builtins::date::date_get_utc_hours:: as *const (), 0), + ( + "getUTCMinutes", + oxide_builtins::date::date_get_utc_minutes:: as *const (), + 0, + ), + ( + "getUTCSeconds", + oxide_builtins::date::date_get_utc_seconds:: as *const (), + 0, + ), + ( + "getUTCMilliseconds", + oxide_builtins::date::date_get_utc_milliseconds:: as *const (), + 0, + ), + ( + "getTimezoneOffset", + oxide_builtins::date::date_get_timezone_offset:: as *const (), + 0, + ), + ("setTime", oxide_builtins::date::date_set_time:: as *const (), 1), + ("setFullYear", oxide_builtins::date::date_set_full_year:: as *const (), 3), + ("setMonth", oxide_builtins::date::date_set_month:: as *const (), 2), + ("setDate", oxide_builtins::date::date_set_date:: as *const (), 1), + ("setHours", oxide_builtins::date::date_set_hours:: as *const (), 4), + ("setMinutes", oxide_builtins::date::date_set_minutes:: as *const (), 3), + ("setSeconds", oxide_builtins::date::date_set_seconds:: as *const (), 2), + ( + "setMilliseconds", + oxide_builtins::date::date_set_milliseconds:: as *const (), + 1, + ), + ("toISOString", oxide_builtins::date::date_to_iso_string:: as *const (), 0), + ("toJSON", oxide_builtins::date::date_to_json:: as *const (), 1), + ("toString", oxide_builtins::date::date_to_string:: as *const (), 0), + ("toDateString", oxide_builtins::date::date_to_date_string:: as *const (), 0), + ("toTimeString", oxide_builtins::date::date_to_time_string:: as *const (), 0), + ("toUTCString", oxide_builtins::date::date_to_utc_string:: as *const (), 0), + ("valueOf", oxide_builtins::date::date_value_of:: as *const (), 0), ], ); diff --git a/crates/oxide_vm/src/bindings/bind_error.rs b/crates/oxide_vm/src/bindings/bind_error.rs index e6d1e51..d3af2fa 100644 --- a/crates/oxide_vm/src/bindings/bind_error.rs +++ b/crates/oxide_vm/src/bindings/bind_error.rs @@ -45,15 +45,15 @@ fn bind_error_subtype_constructor( pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut JsObject) { let error_methods = ErrorMethods { - error: crate::builtins::error::error_constructor as *const (), - type_error: crate::builtins::error::type_error_constructor as *const (), - reference_error: crate::builtins::error::reference_error_constructor as *const (), - range_error: crate::builtins::error::range_error_constructor as *const (), - syntax_error: crate::builtins::error::syntax_error_constructor as *const (), - uri_error: crate::builtins::error::uri_error_constructor as *const (), - eval_error: crate::builtins::error::eval_error_constructor as *const (), - to_string: crate::builtins::error::error_to_string as *const (), - stack: crate::builtins::error::error_stack_getter as *const (), + error: oxide_builtins::error::error_constructor:: as *const (), + type_error: oxide_builtins::error::type_error_constructor:: as *const (), + reference_error: oxide_builtins::error::reference_error_constructor:: as *const (), + range_error: oxide_builtins::error::range_error_constructor:: as *const (), + syntax_error: oxide_builtins::error::syntax_error_constructor:: as *const (), + uri_error: oxide_builtins::error::uri_error_constructor:: as *const (), + eval_error: oxide_builtins::error::eval_error_constructor:: as *const (), + to_string: oxide_builtins::error::error_to_string:: as *const (), + stack: oxide_builtins::error::error_stack_getter:: as *const (), }; session.builtin_world().bind_error_methods( &error_methods, @@ -74,7 +74,7 @@ pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut global, "TypeError", session.builtin_world().type_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::type_error_constructor as *const (), + oxide_builtins::error::type_error_constructor:: as *const (), ); bind_error_subtype_constructor( core, @@ -82,7 +82,7 @@ pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut global, "ReferenceError", session.builtin_world().reference_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::reference_error_constructor as *const (), + oxide_builtins::error::reference_error_constructor:: as *const (), ); bind_error_subtype_constructor( core, @@ -90,7 +90,7 @@ pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut global, "RangeError", session.builtin_world().range_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::range_error_constructor as *const (), + oxide_builtins::error::range_error_constructor:: as *const (), ); bind_error_subtype_constructor( core, @@ -98,7 +98,7 @@ pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut global, "SyntaxError", session.builtin_world().syntax_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::syntax_error_constructor as *const (), + oxide_builtins::error::syntax_error_constructor:: as *const (), ); bind_error_subtype_constructor( core, @@ -106,7 +106,7 @@ pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut global, "URIError", session.builtin_world().uri_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::uri_error_constructor as *const (), + oxide_builtins::error::uri_error_constructor:: as *const (), ); bind_error_subtype_constructor( core, @@ -114,7 +114,7 @@ pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut global, "EvalError", session.builtin_world().eval_error_proto.as_ptr() as *mut JsObject, - crate::builtins::error::eval_error_constructor as *const (), + oxide_builtins::error::eval_error_constructor:: as *const (), ); { @@ -122,7 +122,7 @@ pub fn bind_error(core: &Arc, session: &KernelSession, global: &mut let err_ctor = unsafe { &mut *err_ctor_ptr }; // SAFETY: error_constructor is a NativeFn fn-item. err_ctor.set_native_fn(Some(unsafe { - NativeFnPtr::from_raw(crate::builtins::error::error_constructor as *const ()) + NativeFnPtr::from_raw(oxide_builtins::error::error_constructor:: as *const ()) })); } } diff --git a/crates/oxide_vm/src/bindings/bind_function.rs b/crates/oxide_vm/src/bindings/bind_function.rs index 30867e3..e077774 100644 --- a/crates/oxide_vm/src/bindings/bind_function.rs +++ b/crates/oxide_vm/src/bindings/bind_function.rs @@ -9,10 +9,10 @@ use super::bind_global_value; pub fn bind_function(core: &Arc, session: &KernelSession, global: &mut JsObject) { let function_methods = FunctionMethods { - call: crate::builtins::function::function_call as *const (), - apply: crate::builtins::function::function_apply as *const (), - bind: crate::builtins::function::function_bind as *const (), - to_string: crate::builtins::function::function_to_string as *const (), + call: oxide_builtins::function::function_call:: as *const (), + apply: oxide_builtins::function::function_apply:: as *const (), + bind: oxide_builtins::function::function_bind:: as *const (), + to_string: oxide_builtins::function::function_to_string:: as *const (), }; session.builtin_world().bind_function_methods( &function_methods, diff --git a/crates/oxide_vm/src/bindings/bind_global.rs b/crates/oxide_vm/src/bindings/bind_global.rs index 41850ca..2322625 100644 --- a/crates/oxide_vm/src/bindings/bind_global.rs +++ b/crates/oxide_vm/src/bindings/bind_global.rs @@ -11,12 +11,20 @@ pub fn bind_global(core: &Arc, session: &KernelSession, global: &mut global, core, &[ - ("escape", crate::builtins::global::js_escape as *const (), 1), - ("unescape", crate::builtins::global::js_unescape as *const (), 1), - ("encodeURI", crate::builtins::global::encode_uri as *const (), 1), - ("decodeURI", crate::builtins::global::decode_uri as *const (), 1), - ("encodeURIComponent", crate::builtins::global::encode_uri_component as *const (), 1), - ("decodeURIComponent", crate::builtins::global::decode_uri_component as *const (), 1), + ("escape", oxide_builtins::global::js_escape:: as *const (), 1), + ("unescape", oxide_builtins::global::js_unescape:: as *const (), 1), + ("encodeURI", oxide_builtins::global::encode_uri:: as *const (), 1), + ("decodeURI", oxide_builtins::global::decode_uri:: as *const (), 1), + ( + "encodeURIComponent", + oxide_builtins::global::encode_uri_component:: as *const (), + 1, + ), + ( + "decodeURIComponent", + oxide_builtins::global::decode_uri_component:: as *const (), + 1, + ), ], ); } diff --git a/crates/oxide_vm/src/bindings/bind_iterator.rs b/crates/oxide_vm/src/bindings/bind_iterator.rs index dbcd263..51d270f 100644 --- a/crates/oxide_vm/src/bindings/bind_iterator.rs +++ b/crates/oxide_vm/src/bindings/bind_iterator.rs @@ -11,13 +11,17 @@ pub fn bind_iterator(core: &Arc, session: &KernelSession, global: &m let function_proto = session.builtin_world().function_proto.as_ptr() as *mut JsObject; let mut iterator = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto))); iterator.set_function(true); - configure_native_constructor(&mut iterator, crate::builtins::iterator::iterator_constructor as *const (), 0); + configure_native_constructor( + &mut iterator, + oxide_builtins::iterator::iterator_constructor:: as *const (), + 0, + ); apply_binding_table( session.builtin_world(), &mut iterator, core, - &[("from", crate::builtins::iterator::iterator_from as *const (), 1)], + &[("from", oxide_builtins::iterator::iterator_from:: as *const (), 1)], ); bind_global_value(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator))); diff --git a/crates/oxide_vm/src/bindings/bind_json.rs b/crates/oxide_vm/src/bindings/bind_json.rs index 7dcd285..01c4b86 100644 --- a/crates/oxide_vm/src/bindings/bind_json.rs +++ b/crates/oxide_vm/src/bindings/bind_json.rs @@ -14,8 +14,8 @@ pub fn bind_json(core: &Arc, session: &KernelSession, global: &mut J json, core, &[ - ("parse", crate::builtins::json::json_parse as *const (), 1), - ("stringify", crate::builtins::json::json_stringify as *const (), 1), + ("parse", oxide_builtins::json::json_parse:: as *const (), 1), + ("stringify", oxide_builtins::json::json_stringify:: as *const (), 1), ], ); diff --git a/crates/oxide_vm/src/bindings/bind_map.rs b/crates/oxide_vm/src/bindings/bind_map.rs index a567fe2..c1bb13b 100644 --- a/crates/oxide_vm/src/bindings/bind_map.rs +++ b/crates/oxide_vm/src/bindings/bind_map.rs @@ -10,7 +10,7 @@ pub fn bind_map(core: &Arc, session: &KernelSession, global: &mut Js let ctor = unsafe { &mut *ctor_ptr }; let proto_ptr = session.builtin_world().map_proto.as_ptr() as *mut JsObject; - configure_native_constructor(ctor, crate::builtins::map::map_constructor as *const (), 1); + configure_native_constructor(ctor, oxide_builtins::map::map_constructor:: as *const (), 1); let proto = unsafe { &mut *proto_ptr }; apply_binding_table( @@ -18,14 +18,14 @@ pub fn bind_map(core: &Arc, session: &KernelSession, global: &mut Js proto, core, &[ - ("set", crate::builtins::map::map_set as *const (), 2), - ("get", crate::builtins::map::map_get as *const (), 1), - ("has", crate::builtins::map::map_has as *const (), 1), - ("delete", crate::builtins::map::map_delete as *const (), 1), - ("clear", crate::builtins::map::map_clear as *const (), 0), - ("size", crate::builtins::map::map_size as *const (), 0), + ("set", oxide_builtins::map::map_set:: as *const (), 2), + ("get", oxide_builtins::map::map_get:: as *const (), 1), + ("has", oxide_builtins::map::map_has:: as *const (), 1), + ("delete", oxide_builtins::map::map_delete:: as *const (), 1), + ("clear", oxide_builtins::map::map_clear:: as *const (), 0), + ("size", oxide_builtins::map::map_size:: as *const (), 0), ], ); - bind_constructor!(core, global, "Map", ctor_ptr, crate::builtins::map::map_constructor, 1, hash: true); + bind_constructor!(core, global, "Map", ctor_ptr, oxide_builtins::map::map_constructor::, 1, hash: true); } diff --git a/crates/oxide_vm/src/bindings/bind_math.rs b/crates/oxide_vm/src/bindings/bind_math.rs index 6e00bcd..d82e040 100644 --- a/crates/oxide_vm/src/bindings/bind_math.rs +++ b/crates/oxide_vm/src/bindings/bind_math.rs @@ -18,41 +18,41 @@ pub fn bind_math(core: &Arc, session: &KernelSession, global: &mut J math, sf, sh, - ("abs", crate::builtins::math::math_abs, 1), - ("acos", crate::builtins::math::math_acos, 1), - ("asin", crate::builtins::math::math_asin, 1), - ("atan", crate::builtins::math::math_atan, 1), - ("atan2", crate::builtins::math::math_atan2, 2), - ("cbrt", crate::builtins::math::math_cbrt, 1), - ("ceil", crate::builtins::math::math_ceil, 1), - ("cos", crate::builtins::math::math_cos, 1), - ("cosh", crate::builtins::math::math_cosh, 1), - ("exp", crate::builtins::math::math_exp, 1), - ("floor", crate::builtins::math::math_floor, 1), - ("hypot", crate::builtins::math::math_hypot, 2), - ("imul", crate::builtins::math::math_imul, 2), - ("log", crate::builtins::math::math_log, 1), - ("log10", crate::builtins::math::math_log10, 1), - ("log2", crate::builtins::math::math_log2, 1), - ("max", crate::builtins::math::math_max, 2), - ("min", crate::builtins::math::math_min, 2), - ("pow", crate::builtins::math::math_pow, 2), - ("random", crate::builtins::math::math_random, 0), - ("round", crate::builtins::math::math_round, 1), - ("sign", crate::builtins::math::math_sign, 1), - ("sin", crate::builtins::math::math_sin, 1), - ("sinh", crate::builtins::math::math_sinh, 1), - ("sqrt", crate::builtins::math::math_sqrt, 1), - ("tan", crate::builtins::math::math_tan, 1), - ("tanh", crate::builtins::math::math_tanh, 1), - ("trunc", crate::builtins::math::math_trunc, 1), - ("acosh", crate::builtins::math::math_acosh, 1), - ("asinh", crate::builtins::math::math_asinh, 1), - ("atanh", crate::builtins::math::math_atanh, 1), - ("clz32", crate::builtins::math::math_clz32, 1), - ("expm1", crate::builtins::math::math_expm1, 1), - ("fround", crate::builtins::math::math_fround, 1), - ("log1p", crate::builtins::math::math_log1p, 1), + ("abs", oxide_builtins::math::math_abs::, 1), + ("acos", oxide_builtins::math::math_acos::, 1), + ("asin", oxide_builtins::math::math_asin::, 1), + ("atan", oxide_builtins::math::math_atan::, 1), + ("atan2", oxide_builtins::math::math_atan2::, 2), + ("cbrt", oxide_builtins::math::math_cbrt::, 1), + ("ceil", oxide_builtins::math::math_ceil::, 1), + ("cos", oxide_builtins::math::math_cos::, 1), + ("cosh", oxide_builtins::math::math_cosh::, 1), + ("exp", oxide_builtins::math::math_exp::, 1), + ("floor", oxide_builtins::math::math_floor::, 1), + ("hypot", oxide_builtins::math::math_hypot::, 2), + ("imul", oxide_builtins::math::math_imul::, 2), + ("log", oxide_builtins::math::math_log::, 1), + ("log10", oxide_builtins::math::math_log10::, 1), + ("log2", oxide_builtins::math::math_log2::, 1), + ("max", oxide_builtins::math::math_max::, 2), + ("min", oxide_builtins::math::math_min::, 2), + ("pow", oxide_builtins::math::math_pow::, 2), + ("random", oxide_builtins::math::math_random::, 0), + ("round", oxide_builtins::math::math_round::, 1), + ("sign", oxide_builtins::math::math_sign::, 1), + ("sin", oxide_builtins::math::math_sin::, 1), + ("sinh", oxide_builtins::math::math_sinh::, 1), + ("sqrt", oxide_builtins::math::math_sqrt::, 1), + ("tan", oxide_builtins::math::math_tan::, 1), + ("tanh", oxide_builtins::math::math_tanh::, 1), + ("trunc", oxide_builtins::math::math_trunc::, 1), + ("acosh", oxide_builtins::math::math_acosh::, 1), + ("asinh", oxide_builtins::math::math_asinh::, 1), + ("atanh", oxide_builtins::math::math_atanh::, 1), + ("clz32", oxide_builtins::math::math_clz32::, 1), + ("expm1", oxide_builtins::math::math_expm1::, 1), + ("fround", oxide_builtins::math::math_fround::, 1), + ("log1p", oxide_builtins::math::math_log1p::, 1), ); for (name, val) in [ diff --git a/crates/oxide_vm/src/bindings/bind_number.rs b/crates/oxide_vm/src/bindings/bind_number.rs index df3b390..e693de5 100644 --- a/crates/oxide_vm/src/bindings/bind_number.rs +++ b/crates/oxide_vm/src/bindings/bind_number.rs @@ -12,17 +12,21 @@ pub fn bind_number(core: &Arc, session: &KernelSession, global: &mut let proto_ptr = session.builtin_world().number_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - configure_native_constructor(ctor, crate::builtins::number::number_constructor as *const (), 1); + configure_native_constructor(ctor, oxide_builtins::number::number_constructor:: as *const (), 1); apply_binding_table( session.builtin_world(), ctor, core, &[ - ("isNaN", crate::builtins::number::number_is_nan as *const (), 1), - ("isFinite", crate::builtins::number::number_is_finite as *const (), 1), - ("isInteger", crate::builtins::number::number_is_integer as *const (), 1), - ("isSafeInteger", crate::builtins::number::number_is_safe_integer as *const (), 1), + ("isNaN", oxide_builtins::number::number_is_nan:: as *const (), 1), + ("isFinite", oxide_builtins::number::number_is_finite:: as *const (), 1), + ("isInteger", oxide_builtins::number::number_is_integer:: as *const (), 1), + ( + "isSafeInteger", + oxide_builtins::number::number_is_safe_integer:: as *const (), + 1, + ), ], ); @@ -31,11 +35,19 @@ pub fn bind_number(core: &Arc, session: &KernelSession, global: &mut proto, core, &[ - ("toString", crate::builtins::number::number_to_string as *const (), 0), - ("toFixed", crate::builtins::number::number_to_fixed as *const (), 0), - ("toExponential", crate::builtins::number::number_to_exponential as *const (), 0), - ("toPrecision", crate::builtins::number::number_to_precision as *const (), 0), - ("valueOf", crate::builtins::number::number_value_of as *const (), 0), + ("toString", oxide_builtins::number::number_to_string:: as *const (), 0), + ("toFixed", oxide_builtins::number::number_to_fixed:: as *const (), 0), + ( + "toExponential", + oxide_builtins::number::number_to_exponential:: as *const (), + 0, + ), + ( + "toPrecision", + oxide_builtins::number::number_to_precision:: as *const (), + 0, + ), + ("valueOf", oxide_builtins::number::number_value_of:: as *const (), 0), ], ); @@ -55,15 +67,15 @@ pub fn bind_number(core: &Arc, session: &KernelSession, global: &mut ctor.set_shape_id(next_shape); } - bind_constructor!(core, global, "Number", ctor_ptr, crate::builtins::number::number_constructor, 1, hash: true); + bind_constructor!(core, global, "Number", ctor_ptr, oxide_builtins::number::number_constructor::, 1, hash: true); apply_binding_table( session.builtin_world(), global, core, &[ - ("parseInt", crate::builtins::number::number_parse_int as *const (), 1), - ("parseFloat", crate::builtins::number::number_parse_float as *const (), 1), + ("parseInt", oxide_builtins::number::number_parse_int:: as *const (), 1), + ("parseFloat", oxide_builtins::number::number_parse_float:: as *const (), 1), ], ); } diff --git a/crates/oxide_vm/src/bindings/bind_object.rs b/crates/oxide_vm/src/bindings/bind_object.rs index c976e17..12a190a 100644 --- a/crates/oxide_vm/src/bindings/bind_object.rs +++ b/crates/oxide_vm/src/bindings/bind_object.rs @@ -8,32 +8,41 @@ use crate::bind_constructor; pub fn bind_object(core: &Arc, session: &KernelSession, global: &mut JsObject) { let methods = ObjectMethods { - keys: crate::builtins::object::object_keys as *const (), - create: crate::builtins::object::object_create as *const (), - assign: crate::builtins::object::object_assign as *const (), - is: crate::builtins::object::object_is as *const (), - define_property: crate::builtins::object::object_define_property as *const (), - get_own_property_descriptor: crate::builtins::object::object_get_own_property_descriptor as *const (), - freeze: crate::builtins::object::object_freeze as *const (), - seal: crate::builtins::object::object_seal as *const (), - prevent_extensions: crate::builtins::object::object_prevent_extensions as *const (), - is_frozen: crate::builtins::object::object_is_frozen as *const (), - is_sealed: crate::builtins::object::object_is_sealed as *const (), - is_extensible: crate::builtins::object::object_is_extensible as *const (), - get_own_property_names: crate::builtins::object::object_get_own_property_names as *const (), - define_properties: crate::builtins::object::object_define_properties as *const (), - from_entries: crate::builtins::object::object_from_entries as *const (), - get_prototype_of: crate::builtins::object::object_get_prototype_of as *const (), - has_own: crate::builtins::object::object_has_own as *const (), - entries: crate::builtins::object::object_entries as *const (), - values: crate::builtins::object::object_values as *const (), - has_own_property: crate::builtins::object::object_proto_has_own_property as *const (), - property_is_enumerable: crate::builtins::object::object_proto_property_is_enumerable as *const (), + keys: oxide_builtins::object::object_keys:: as *const (), + create: oxide_builtins::object::object_create:: as *const (), + assign: oxide_builtins::object::object_assign:: as *const (), + is: oxide_builtins::object::object_is:: as *const (), + define_property: oxide_builtins::object::object_define_property:: as *const (), + get_own_property_descriptor: oxide_builtins::object::object_get_own_property_descriptor:: + as *const (), + freeze: oxide_builtins::object::object_freeze:: as *const (), + seal: oxide_builtins::object::object_seal:: as *const (), + prevent_extensions: oxide_builtins::object::object_prevent_extensions:: as *const (), + is_frozen: oxide_builtins::object::object_is_frozen:: as *const (), + is_sealed: oxide_builtins::object::object_is_sealed:: as *const (), + is_extensible: oxide_builtins::object::object_is_extensible:: as *const (), + get_own_property_names: oxide_builtins::object::object_get_own_property_names:: as *const (), + define_properties: oxide_builtins::object::object_define_properties:: as *const (), + from_entries: oxide_builtins::object::object_from_entries:: as *const (), + get_prototype_of: oxide_builtins::object::object_get_prototype_of:: as *const (), + has_own: oxide_builtins::object::object_has_own:: as *const (), + entries: oxide_builtins::object::object_entries:: as *const (), + values: oxide_builtins::object::object_values:: as *const (), + has_own_property: oxide_builtins::object::object_proto_has_own_property:: as *const (), + property_is_enumerable: oxide_builtins::object::object_proto_property_is_enumerable:: + as *const (), }; session .builtin_world() .bind_object_methods(&methods, core.perm_interner().as_ref(), core.shape_forge().as_ref()); let ctor_ptr = session.builtin_world().object_constructor.as_ptr() as *mut JsObject; - bind_constructor!(core, global, "Object", ctor_ptr, crate::builtins::object::object_constructor, 1); + bind_constructor!( + core, + global, + "Object", + ctor_ptr, + oxide_builtins::object::object_constructor::, + 1 + ); } diff --git a/crates/oxide_vm/src/bindings/bind_reflect.rs b/crates/oxide_vm/src/bindings/bind_reflect.rs index bc8bb5c..d4283d4 100644 --- a/crates/oxide_vm/src/bindings/bind_reflect.rs +++ b/crates/oxide_vm/src/bindings/bind_reflect.rs @@ -17,23 +17,47 @@ pub fn bind_reflect(core: &Arc, session: &KernelSession, global: &mu &mut reflect, core, &[ - ("apply", crate::builtins::reflect::reflect_apply as *const (), 3), - ("construct", crate::builtins::reflect::reflect_construct as *const (), 2), - ("defineProperty", crate::builtins::reflect::reflect_define_property as *const (), 3), - ("deleteProperty", crate::builtins::reflect::reflect_delete_property as *const (), 2), - ("get", crate::builtins::reflect::reflect_get as *const (), 2), + ("apply", oxide_builtins::reflect::reflect_apply:: as *const (), 3), + ("construct", oxide_builtins::reflect::reflect_construct:: as *const (), 2), ( - "getOwnPropertyDescriptor", - crate::builtins::reflect::reflect_get_own_property_descriptor as *const (), + "defineProperty", + oxide_builtins::reflect::reflect_define_property:: as *const (), + 3, + ), + ( + "deleteProperty", + oxide_builtins::reflect::reflect_delete_property:: as *const (), + 2, + ), + ("get", oxide_builtins::reflect::reflect_get:: as *const (), 2), + ( + "getOwnPropertyDescriptor", + oxide_builtins::reflect::reflect_get_own_property_descriptor:: as *const (), + 2, + ), + ( + "getPrototypeOf", + oxide_builtins::reflect::reflect_get_prototype_of:: as *const (), + 1, + ), + ("has", oxide_builtins::reflect::reflect_has:: as *const (), 2), + ( + "isExtensible", + oxide_builtins::reflect::reflect_is_extensible:: as *const (), + 1, + ), + ("ownKeys", oxide_builtins::reflect::reflect_own_keys:: as *const (), 1), + ( + "preventExtensions", + oxide_builtins::reflect::reflect_prevent_extensions:: as *const (), + 1, + ), + ("set", oxide_builtins::reflect::reflect_set:: as *const (), 3), + ( + "setPrototypeOf", + oxide_builtins::reflect::reflect_set_prototype_of:: as *const (), 2, ), - ("getPrototypeOf", crate::builtins::reflect::reflect_get_prototype_of as *const (), 1), - ("has", crate::builtins::reflect::reflect_has as *const (), 2), - ("isExtensible", crate::builtins::reflect::reflect_is_extensible as *const (), 1), - ("ownKeys", crate::builtins::reflect::reflect_own_keys as *const (), 1), - ("preventExtensions", crate::builtins::reflect::reflect_prevent_extensions as *const (), 1), - ("set", crate::builtins::reflect::reflect_set as *const (), 3), - ("setPrototypeOf", crate::builtins::reflect::reflect_set_prototype_of as *const (), 2), ], ); diff --git a/crates/oxide_vm/src/bindings/bind_regexp.rs b/crates/oxide_vm/src/bindings/bind_regexp.rs index a3c80a8..8674978 100644 --- a/crates/oxide_vm/src/bindings/bind_regexp.rs +++ b/crates/oxide_vm/src/bindings/bind_regexp.rs @@ -12,18 +12,18 @@ pub fn bind_regexp(core: &Arc, session: &KernelSession, global: &mut let proto_ptr = session.builtin_world().regexp_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - configure_native_constructor(ctor, crate::builtins::regexp::regexp_constructor as *const (), 2); + configure_native_constructor(ctor, oxide_builtins::regexp::regexp_constructor:: as *const (), 2); apply_binding_table( session.builtin_world(), proto, core, &[ - ("exec", crate::builtins::regexp::regexp_exec as *const (), 1), - ("test", crate::builtins::regexp::regexp_test as *const (), 1), - ("toString", crate::builtins::regexp::regexp_to_string as *const (), 0), + ("exec", oxide_builtins::regexp::regexp_exec:: as *const (), 1), + ("test", oxide_builtins::regexp::regexp_test:: as *const (), 1), + ("toString", oxide_builtins::regexp::regexp_to_string:: as *const (), 0), ], ); - bind_constructor!(core, global, "RegExp", ctor_ptr, crate::builtins::regexp::regexp_constructor, 2, hash: true); + bind_constructor!(core, global, "RegExp", ctor_ptr, oxide_builtins::regexp::regexp_constructor::, 2, hash: true); } diff --git a/crates/oxide_vm/src/bindings/bind_set.rs b/crates/oxide_vm/src/bindings/bind_set.rs index 0e51ffc..078d89c 100644 --- a/crates/oxide_vm/src/bindings/bind_set.rs +++ b/crates/oxide_vm/src/bindings/bind_set.rs @@ -10,7 +10,7 @@ pub fn bind_set(core: &Arc, session: &KernelSession, global: &mut Js let ctor = unsafe { &mut *ctor_ptr }; let proto_ptr = session.builtin_world().set_proto.as_ptr() as *mut JsObject; - configure_native_constructor(ctor, crate::builtins::set::set_constructor as *const (), 1); + configure_native_constructor(ctor, oxide_builtins::set::set_constructor:: as *const (), 1); let proto = unsafe { &mut *proto_ptr }; apply_binding_table( @@ -18,13 +18,13 @@ pub fn bind_set(core: &Arc, session: &KernelSession, global: &mut Js proto, core, &[ - ("add", crate::builtins::set::set_add as *const (), 1), - ("has", crate::builtins::set::set_has as *const (), 1), - ("delete", crate::builtins::set::set_delete as *const (), 1), - ("clear", crate::builtins::set::set_clear as *const (), 0), - ("size", crate::builtins::set::set_size as *const (), 0), + ("add", oxide_builtins::set::set_add:: as *const (), 1), + ("has", oxide_builtins::set::set_has:: as *const (), 1), + ("delete", oxide_builtins::set::set_delete:: as *const (), 1), + ("clear", oxide_builtins::set::set_clear:: as *const (), 0), + ("size", oxide_builtins::set::set_size:: as *const (), 0), ], ); - bind_constructor!(core, global, "Set", ctor_ptr, crate::builtins::set::set_constructor, 1, hash: true); + bind_constructor!(core, global, "Set", ctor_ptr, oxide_builtins::set::set_constructor::, 1, hash: true); } diff --git a/crates/oxide_vm/src/bindings/bind_string.rs b/crates/oxide_vm/src/bindings/bind_string.rs index 01dd6a2..c58f7b2 100644 --- a/crates/oxide_vm/src/bindings/bind_string.rs +++ b/crates/oxide_vm/src/bindings/bind_string.rs @@ -8,33 +8,33 @@ use oxide_types::value::JsValue; pub fn bind_string(core: &Arc, session: &KernelSession, global: &mut JsObject) { let string_methods = StringMethods { - from_char_code: crate::builtins::string::string_from_char_code as *const (), - index_of: crate::builtins::string::string_index_of as *const (), - includes: crate::builtins::string::string_includes as *const (), - char_at: crate::builtins::string::string_char_at as *const (), - char_code_at: crate::builtins::string::string_char_code_at as *const (), - concat: crate::builtins::string::string_concat as *const (), - slice: crate::builtins::string::string_slice as *const (), - substring: crate::builtins::string::string_substring as *const (), - to_upper_case: crate::builtins::string::string_to_upper_case as *const (), - to_lower_case: crate::builtins::string::string_to_lower_case as *const (), - trim: crate::builtins::string::string_trim as *const (), - repeat: crate::builtins::string::string_repeat as *const (), - pad_start: crate::builtins::string::string_pad_start as *const (), - pad_end: crate::builtins::string::string_pad_end as *const (), - starts_with: crate::builtins::string::string_starts_with as *const (), - ends_with: crate::builtins::string::string_ends_with as *const (), - split: crate::builtins::string::string_split as *const (), - replace: crate::builtins::string::string_replace as *const (), - match_fn: crate::builtins::string::string_match_fn as *const (), - search: crate::builtins::string::string_search as *const (), - trim_start: crate::builtins::string::string_trim_start as *const (), - trim_end: crate::builtins::string::string_trim_end as *const (), - code_point_at: crate::builtins::string::string_code_point_at as *const (), - normalize: crate::builtins::string::string_normalize as *const (), - match_all: crate::builtins::string::string_match_all as *const (), - replace_all: crate::builtins::string::string_replace_all as *const (), - value_of: crate::builtins::string::string_value_of as *const (), + from_char_code: oxide_builtins::string::string_from_char_code:: as *const (), + index_of: oxide_builtins::string::string_index_of:: as *const (), + includes: oxide_builtins::string::string_includes:: as *const (), + char_at: oxide_builtins::string::string_char_at:: as *const (), + char_code_at: oxide_builtins::string::string_char_code_at:: as *const (), + concat: oxide_builtins::string::string_concat:: as *const (), + slice: oxide_builtins::string::string_slice:: as *const (), + substring: oxide_builtins::string::string_substring:: as *const (), + to_upper_case: oxide_builtins::string::string_to_upper_case:: as *const (), + to_lower_case: oxide_builtins::string::string_to_lower_case:: as *const (), + trim: oxide_builtins::string::string_trim:: as *const (), + repeat: oxide_builtins::string::string_repeat:: as *const (), + pad_start: oxide_builtins::string::string_pad_start:: as *const (), + pad_end: oxide_builtins::string::string_pad_end:: as *const (), + starts_with: oxide_builtins::string::string_starts_with:: as *const (), + ends_with: oxide_builtins::string::string_ends_with:: as *const (), + split: oxide_builtins::string::string_split:: as *const (), + replace: oxide_builtins::string::string_replace:: as *const (), + match_fn: oxide_builtins::string::string_match_fn:: as *const (), + search: oxide_builtins::string::string_search:: as *const (), + trim_start: oxide_builtins::string::string_trim_start:: as *const (), + trim_end: oxide_builtins::string::string_trim_end:: as *const (), + code_point_at: oxide_builtins::string::string_code_point_at:: as *const (), + normalize: oxide_builtins::string::string_normalize:: as *const (), + match_all: oxide_builtins::string::string_match_all:: as *const (), + replace_all: oxide_builtins::string::string_replace_all:: as *const (), + value_of: oxide_builtins::string::string_value_of:: as *const (), }; session.builtin_world().bind_string_methods( &string_methods, @@ -44,7 +44,7 @@ pub fn bind_string(core: &Arc, session: &KernelSession, global: &mut let ctor_ptr = session.builtin_world().string_constructor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; - configure_native_constructor(ctor, crate::builtins::string::string_constructor as *const (), 1); + configure_native_constructor(ctor, oxide_builtins::string::string_constructor:: as *const (), 1); let proto_ptr = session.builtin_world().string_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; @@ -52,7 +52,7 @@ pub fn bind_string(core: &Arc, session: &KernelSession, global: &mut session.builtin_world(), proto, core, - &[("toString", crate::builtins::string::string_to_string as *const (), 0)], + &[("toString", oxide_builtins::string::string_to_string:: as *const (), 0)], ); let si_str = core.perm_interner().intern("String").0; diff --git a/crates/oxide_vm/src/bindings/bind_stubs.rs b/crates/oxide_vm/src/bindings/bind_stubs.rs index 0bb7d8b..14625c4 100644 --- a/crates/oxide_vm/src/bindings/bind_stubs.rs +++ b/crates/oxide_vm/src/bindings/bind_stubs.rs @@ -9,14 +9,22 @@ use oxide_types::value::JsValue; use crate::bindings::{bind_global_value, configure_native_constructor}; const STUBS: [(&str, *const (), u8); 8] = [ - ("Proxy", crate::builtins::stubs::proxy_stub as *const (), 2), - ("BigInt", crate::builtins::stubs::bigint_stub as *const (), 1), - ("WeakMap", crate::builtins::stubs::weakmap_stub as *const (), 0), - ("WeakSet", crate::builtins::stubs::weakset_stub as *const (), 0), - ("WeakRef", crate::builtins::stubs::weakref_stub as *const (), 1), - ("FinalizationRegistry", crate::builtins::stubs::finalization_registry_stub as *const (), 1), - ("SharedArrayBuffer", crate::builtins::stubs::shared_array_buffer_stub as *const (), 1), - ("Atomics", crate::builtins::stubs::atomics_stub as *const (), 0), + ("Proxy", oxide_builtins::stubs::proxy_stub:: as *const (), 2), + ("BigInt", oxide_builtins::stubs::bigint_stub:: as *const (), 1), + ("WeakMap", oxide_builtins::stubs::weakmap_stub:: as *const (), 0), + ("WeakSet", oxide_builtins::stubs::weakset_stub:: as *const (), 0), + ("WeakRef", oxide_builtins::stubs::weakref_stub:: as *const (), 1), + ( + "FinalizationRegistry", + oxide_builtins::stubs::finalization_registry_stub:: as *const (), + 1, + ), + ( + "SharedArrayBuffer", + oxide_builtins::stubs::shared_array_buffer_stub:: as *const (), + 1, + ), + ("Atomics", oxide_builtins::stubs::atomics_stub:: as *const (), 0), ]; pub fn bind_stubs(core: &Arc, session: &mut KernelSession, global: &mut JsObject) { diff --git a/crates/oxide_vm/src/bindings/bind_symbol.rs b/crates/oxide_vm/src/bindings/bind_symbol.rs index fd6180d..1b020df 100644 --- a/crates/oxide_vm/src/bindings/bind_symbol.rs +++ b/crates/oxide_vm/src/bindings/bind_symbol.rs @@ -13,15 +13,15 @@ pub fn bind_symbol(core: &Arc, session: &KernelSession, global: &mut let proto_ptr = session.builtin_world().symbol_proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; - configure_native_constructor(ctor, crate::builtins::symbol::symbol_constructor as *const (), 1); + configure_native_constructor(ctor, oxide_builtins::symbol::symbol_constructor:: as *const (), 1); apply_binding_table( session.builtin_world(), ctor, core, &[ - ("for", crate::builtins::symbol::symbol_for as *const (), 1), - ("keyFor", crate::builtins::symbol::symbol_key_for as *const (), 1), + ("for", oxide_builtins::symbol::symbol_for:: as *const (), 1), + ("keyFor", oxide_builtins::symbol::symbol_key_for:: as *const (), 1), ], ); @@ -29,7 +29,7 @@ pub fn bind_symbol(core: &Arc, session: &KernelSession, global: &mut session.builtin_world(), proto, core, - &[("toString", crate::builtins::symbol::symbol_to_string as *const (), 0)], + &[("toString", oxide_builtins::symbol::symbol_to_string:: as *const (), 0)], ); for (name, val) in [ @@ -57,7 +57,7 @@ pub fn bind_symbol(core: &Arc, session: &KernelSession, global: &mut bind_well_known_symbol(core, ctor, name, val); } - bind_constructor!(core, global, "Symbol", ctor_ptr, crate::builtins::symbol::symbol_constructor, 1, hash: true); + bind_constructor!(core, global, "Symbol", ctor_ptr, oxide_builtins::symbol::symbol_constructor::, 1, hash: true); } fn bind_well_known_symbol(core: &Arc, ctor: &mut JsObject, name: &str, val: JsValue) { diff --git a/crates/oxide_vm/src/bindings/bind_typed_array.rs b/crates/oxide_vm/src/bindings/bind_typed_array.rs index dcd8a88..0d93c80 100644 --- a/crates/oxide_vm/src/bindings/bind_typed_array.rs +++ b/crates/oxide_vm/src/bindings/bind_typed_array.rs @@ -8,7 +8,7 @@ use oxide_types::object::JsObject; macro_rules! bind_typed_array_constructor { ($core:expr, $global:expr, $name:literal, $ctor_ptr:expr, $ctor_fn:path) => {{ let ctor = unsafe { &mut *$ctor_ptr }; - configure_native_constructor(ctor, $ctor_fn as *const (), 1); + configure_native_constructor(ctor, ($ctor_fn as fn(&mut $crate::vm::Vm, &[u8]) -> oxide_runtime_api::NativeResult) as *const (), 1); bind_constructor!($core, $global, $name, $ctor_ptr, $ctor_fn, 1, hash: true); }}; } @@ -21,12 +21,20 @@ pub fn bind_typed_array(core: &Arc, session: &KernelSession, global: shared_proto, core, &[ - ("at", crate::builtins::typed_array::typed_array_at as *const (), 1), - ("fill", crate::builtins::typed_array::typed_array_fill as *const (), 3), - ("set", crate::builtins::typed_array::typed_array_set as *const (), 2), - ("slice", crate::builtins::typed_array::typed_array_slice as *const (), 2), - ("subarray", crate::builtins::typed_array::typed_array_subarray as *const (), 2), - ("toString", crate::builtins::typed_array::typed_array_to_string as *const (), 0), + ("at", oxide_builtins::typed_array::typed_array_at:: as *const (), 1), + ("fill", oxide_builtins::typed_array::typed_array_fill:: as *const (), 3), + ("set", oxide_builtins::typed_array::typed_array_set:: as *const (), 2), + ("slice", oxide_builtins::typed_array::typed_array_slice:: as *const (), 2), + ( + "subarray", + oxide_builtins::typed_array::typed_array_subarray:: as *const (), + 2, + ), + ( + "toString", + oxide_builtins::typed_array::typed_array_to_string:: as *const (), + 0, + ), ], ); @@ -35,76 +43,76 @@ pub fn bind_typed_array(core: &Arc, session: &KernelSession, global: global, "Int8Array", session.builtin_world().int8array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::int8array_constructor + oxide_builtins::typed_array::int8array_constructor ); bind_typed_array_constructor!( core, global, "Uint8Array", session.builtin_world().uint8array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::uint8array_constructor + oxide_builtins::typed_array::uint8array_constructor ); bind_typed_array_constructor!( core, global, "Uint8ClampedArray", session.builtin_world().uint8clampedarray_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::uint8clampedarray_constructor + oxide_builtins::typed_array::uint8clampedarray_constructor ); bind_typed_array_constructor!( core, global, "Int16Array", session.builtin_world().int16array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::int16array_constructor + oxide_builtins::typed_array::int16array_constructor ); bind_typed_array_constructor!( core, global, "Uint16Array", session.builtin_world().uint16array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::uint16array_constructor + oxide_builtins::typed_array::uint16array_constructor ); bind_typed_array_constructor!( core, global, "Int32Array", session.builtin_world().int32array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::int32array_constructor + oxide_builtins::typed_array::int32array_constructor ); bind_typed_array_constructor!( core, global, "Uint32Array", session.builtin_world().uint32array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::uint32array_constructor + oxide_builtins::typed_array::uint32array_constructor ); bind_typed_array_constructor!( core, global, "Float32Array", session.builtin_world().float32array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::float32array_constructor + oxide_builtins::typed_array::float32array_constructor ); bind_typed_array_constructor!( core, global, "Float64Array", session.builtin_world().float64array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::float64array_constructor + oxide_builtins::typed_array::float64array_constructor ); bind_typed_array_constructor!( core, global, "BigInt64Array", session.builtin_world().bigint64array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::bigint64array_constructor + oxide_builtins::typed_array::bigint64array_constructor ); bind_typed_array_constructor!( core, global, "BigUint64Array", session.builtin_world().biguint64array_constructor.as_ptr() as *mut JsObject, - crate::builtins::typed_array::biguint64array_constructor + oxide_builtins::typed_array::biguint64array_constructor ); } diff --git a/crates/oxide_vm/src/bindings/mod.rs b/crates/oxide_vm/src/bindings/mod.rs index d399da9..a8eb597 100644 --- a/crates/oxide_vm/src/bindings/mod.rs +++ b/crates/oxide_vm/src/bindings/mod.rs @@ -46,7 +46,8 @@ macro_rules! bind_constructor { $global.push_prop(val); } let ctor = unsafe { &mut *$ctor_ptr }; - ctor.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw($ctor_fn as *const ()) })); + let ptr: *const () = ($ctor_fn as fn(&mut $crate::vm::Vm, &[u8]) -> oxide_runtime_api::NativeResult) as *const (); + ctor.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(ptr) })); ctor.set_native_arg_count($nargs); }}; } @@ -127,14 +128,22 @@ fn bind_global_functions(core: &Arc, session: &KernelSession, global global, core, &[ - ("parseInt", crate::builtins::number::number_parse_int as *const (), 1), - ("parseFloat", crate::builtins::number::number_parse_float as *const (), 1), - ("escape", crate::builtins::global::js_escape as *const (), 1), - ("unescape", crate::builtins::global::js_unescape as *const (), 1), - ("encodeURI", crate::builtins::global::encode_uri as *const (), 1), - ("decodeURI", crate::builtins::global::decode_uri as *const (), 1), - ("encodeURIComponent", crate::builtins::global::encode_uri_component as *const (), 1), - ("decodeURIComponent", crate::builtins::global::decode_uri_component as *const (), 1), + ("parseInt", oxide_builtins::number::number_parse_int:: as *const (), 1), + ("parseFloat", oxide_builtins::number::number_parse_float:: as *const (), 1), + ("escape", oxide_builtins::global::js_escape:: as *const (), 1), + ("unescape", oxide_builtins::global::js_unescape:: as *const (), 1), + ("encodeURI", oxide_builtins::global::encode_uri:: as *const (), 1), + ("decodeURI", oxide_builtins::global::decode_uri:: as *const (), 1), + ( + "encodeURIComponent", + oxide_builtins::global::encode_uri_component:: as *const (), + 1, + ), + ( + "decodeURIComponent", + oxide_builtins::global::decode_uri_component:: as *const (), + 1, + ), ], ); } @@ -149,23 +158,47 @@ fn bind_reflect_global(core: &Arc, session: &KernelSession, global: &mut reflect, core, &[ - ("apply", crate::builtins::reflect::reflect_apply as *const (), 3), - ("construct", crate::builtins::reflect::reflect_construct as *const (), 2), - ("defineProperty", crate::builtins::reflect::reflect_define_property as *const (), 3), - ("deleteProperty", crate::builtins::reflect::reflect_delete_property as *const (), 2), - ("get", crate::builtins::reflect::reflect_get as *const (), 2), + ("apply", oxide_builtins::reflect::reflect_apply:: as *const (), 3), + ("construct", oxide_builtins::reflect::reflect_construct:: as *const (), 2), ( - "getOwnPropertyDescriptor", - crate::builtins::reflect::reflect_get_own_property_descriptor as *const (), + "defineProperty", + oxide_builtins::reflect::reflect_define_property:: as *const (), + 3, + ), + ( + "deleteProperty", + oxide_builtins::reflect::reflect_delete_property:: as *const (), + 2, + ), + ("get", oxide_builtins::reflect::reflect_get:: as *const (), 2), + ( + "getOwnPropertyDescriptor", + oxide_builtins::reflect::reflect_get_own_property_descriptor:: as *const (), + 2, + ), + ( + "getPrototypeOf", + oxide_builtins::reflect::reflect_get_prototype_of:: as *const (), + 1, + ), + ("has", oxide_builtins::reflect::reflect_has:: as *const (), 2), + ( + "isExtensible", + oxide_builtins::reflect::reflect_is_extensible:: as *const (), + 1, + ), + ("ownKeys", oxide_builtins::reflect::reflect_own_keys:: as *const (), 1), + ( + "preventExtensions", + oxide_builtins::reflect::reflect_prevent_extensions:: as *const (), + 1, + ), + ("set", oxide_builtins::reflect::reflect_set:: as *const (), 3), + ( + "setPrototypeOf", + oxide_builtins::reflect::reflect_set_prototype_of:: as *const (), 2, ), - ("getPrototypeOf", crate::builtins::reflect::reflect_get_prototype_of as *const (), 1), - ("has", crate::builtins::reflect::reflect_has as *const (), 2), - ("isExtensible", crate::builtins::reflect::reflect_is_extensible as *const (), 1), - ("ownKeys", crate::builtins::reflect::reflect_own_keys as *const (), 1), - ("preventExtensions", crate::builtins::reflect::reflect_prevent_extensions as *const (), 1), - ("set", crate::builtins::reflect::reflect_set as *const (), 3), - ("setPrototypeOf", crate::builtins::reflect::reflect_set_prototype_of as *const (), 2), ], ); bind_existing_global(core, global, "Reflect", JsValue::from_js_object(Box::into_raw(reflect))); @@ -175,26 +208,38 @@ fn bind_iterator_global(core: &Arc, session: &KernelSession, global: let function_proto = session.builtin_world().function_proto.as_ptr() as *mut JsObject; let mut iterator = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto))); iterator.set_function(true); - configure_native_constructor(&mut iterator, crate::builtins::iterator::iterator_constructor as *const (), 0); + configure_native_constructor( + &mut iterator, + oxide_builtins::iterator::iterator_constructor:: as *const (), + 0, + ); apply_binding_table( session.builtin_world(), &mut iterator, core, - &[("from", crate::builtins::iterator::iterator_from as *const (), 1)], + &[("from", oxide_builtins::iterator::iterator_from:: as *const (), 1)], ); bind_existing_global(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator))); } fn bind_stub_globals(core: &Arc, session: &KernelSession, global: &mut JsObject) { for (index, (name, native_fn, arg_count)) in [ - ("Proxy", crate::builtins::stubs::proxy_stub as *const (), 2), - ("BigInt", crate::builtins::stubs::bigint_stub as *const (), 1), - ("WeakMap", crate::builtins::stubs::weakmap_stub as *const (), 0), - ("WeakSet", crate::builtins::stubs::weakset_stub as *const (), 0), - ("WeakRef", crate::builtins::stubs::weakref_stub as *const (), 1), - ("FinalizationRegistry", crate::builtins::stubs::finalization_registry_stub as *const (), 1), - ("SharedArrayBuffer", crate::builtins::stubs::shared_array_buffer_stub as *const (), 1), - ("Atomics", crate::builtins::stubs::atomics_stub as *const (), 0), + ("Proxy", oxide_builtins::stubs::proxy_stub:: as *const (), 2), + ("BigInt", oxide_builtins::stubs::bigint_stub:: as *const (), 1), + ("WeakMap", oxide_builtins::stubs::weakmap_stub:: as *const (), 0), + ("WeakSet", oxide_builtins::stubs::weakset_stub:: as *const (), 0), + ("WeakRef", oxide_builtins::stubs::weakref_stub:: as *const (), 1), + ( + "FinalizationRegistry", + oxide_builtins::stubs::finalization_registry_stub:: as *const (), + 1, + ), + ( + "SharedArrayBuffer", + oxide_builtins::stubs::shared_array_buffer_stub:: as *const (), + 1, + ), + ("Atomics", oxide_builtins::stubs::atomics_stub:: as *const (), 0), ] .into_iter() .enumerate() @@ -209,26 +254,66 @@ fn bind_stub_globals(core: &Arc, session: &KernelSession, global: &m pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession, global: &mut JsObject) { let world = session.builtin_world(); - configure_existing_ctor(&world.object_constructor, crate::builtins::object::object_constructor as *const (), 1); - configure_existing_ctor(&world.array_constructor, crate::builtins::array::array_constructor as *const (), 1); + configure_existing_ctor( + &world.object_constructor, + oxide_builtins::object::object_constructor:: as *const (), + 1, + ); + configure_existing_ctor( + &world.array_constructor, + oxide_builtins::array::array_constructor:: as *const (), + 1, + ); configure_existing_ctor( &world.array_buffer_constructor, - crate::builtins::array_buffer::array_buffer_constructor as *const (), + oxide_builtins::array_buffer::array_buffer_constructor:: as *const (), 1, ); configure_existing_ctor( &world.data_view_constructor, - crate::builtins::data_view::data_view_constructor as *const (), + oxide_builtins::data_view::data_view_constructor:: as *const (), 3, ); - configure_existing_ctor(&world.error_constructor, crate::builtins::error::error_constructor as *const (), 1); - configure_existing_ctor(&world.number_constructor, crate::builtins::number::number_constructor as *const (), 1); - configure_existing_ctor(&world.date_constructor, crate::builtins::date::date_constructor as *const (), 7); - configure_existing_ctor(&world.set_constructor, crate::builtins::set::set_constructor as *const (), 1); - configure_existing_ctor(&world.map_constructor, crate::builtins::map::map_constructor as *const (), 1); - configure_existing_ctor(&world.boolean_constructor, crate::builtins::boolean::boolean_constructor as *const (), 1); - configure_existing_ctor(&world.regexp_constructor, crate::builtins::regexp::regexp_constructor as *const (), 2); - configure_existing_ctor(&world.symbol_constructor, crate::builtins::symbol::symbol_constructor as *const (), 1); + configure_existing_ctor( + &world.error_constructor, + oxide_builtins::error::error_constructor:: as *const (), + 1, + ); + configure_existing_ctor( + &world.number_constructor, + oxide_builtins::number::number_constructor:: as *const (), + 1, + ); + configure_existing_ctor( + &world.date_constructor, + oxide_builtins::date::date_constructor:: as *const (), + 7, + ); + configure_existing_ctor( + &world.set_constructor, + oxide_builtins::set::set_constructor:: as *const (), + 1, + ); + configure_existing_ctor( + &world.map_constructor, + oxide_builtins::map::map_constructor:: as *const (), + 1, + ); + configure_existing_ctor( + &world.boolean_constructor, + oxide_builtins::boolean::boolean_constructor:: as *const (), + 1, + ); + configure_existing_ctor( + &world.regexp_constructor, + oxide_builtins::regexp::regexp_constructor:: as *const (), + 2, + ); + configure_existing_ctor( + &world.symbol_constructor, + oxide_builtins::symbol::symbol_constructor:: as *const (), + 1, + ); for (name, value) in [ ("Object", JsValue::from_js_object(world.object_constructor.as_ptr() as *mut JsObject)), @@ -258,57 +343,57 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession ( "Int8Array", &world.int8array_constructor, - crate::builtins::typed_array::int8array_constructor as *const (), + oxide_builtins::typed_array::int8array_constructor:: as *const (), ), ( "Uint8Array", &world.uint8array_constructor, - crate::builtins::typed_array::uint8array_constructor as *const (), + oxide_builtins::typed_array::uint8array_constructor:: as *const (), ), ( "Uint8ClampedArray", &world.uint8clampedarray_constructor, - crate::builtins::typed_array::uint8clampedarray_constructor as *const (), + oxide_builtins::typed_array::uint8clampedarray_constructor:: as *const (), ), ( "Int16Array", &world.int16array_constructor, - crate::builtins::typed_array::int16array_constructor as *const (), + oxide_builtins::typed_array::int16array_constructor:: as *const (), ), ( "Uint16Array", &world.uint16array_constructor, - crate::builtins::typed_array::uint16array_constructor as *const (), + oxide_builtins::typed_array::uint16array_constructor:: as *const (), ), ( "Int32Array", &world.int32array_constructor, - crate::builtins::typed_array::int32array_constructor as *const (), + oxide_builtins::typed_array::int32array_constructor:: as *const (), ), ( "Uint32Array", &world.uint32array_constructor, - crate::builtins::typed_array::uint32array_constructor as *const (), + oxide_builtins::typed_array::uint32array_constructor:: as *const (), ), ( "Float32Array", &world.float32array_constructor, - crate::builtins::typed_array::float32array_constructor as *const (), + oxide_builtins::typed_array::float32array_constructor:: as *const (), ), ( "Float64Array", &world.float64array_constructor, - crate::builtins::typed_array::float64array_constructor as *const (), + oxide_builtins::typed_array::float64array_constructor:: as *const (), ), ( "BigInt64Array", &world.bigint64array_constructor, - crate::builtins::typed_array::bigint64array_constructor as *const (), + oxide_builtins::typed_array::bigint64array_constructor:: as *const (), ), ( "BigUint64Array", &world.biguint64array_constructor, - crate::builtins::typed_array::biguint64array_constructor as *const (), + oxide_builtins::typed_array::biguint64array_constructor:: as *const (), ), ] { configure_existing_ctor(ctor, native_fn, 1); @@ -321,7 +406,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession global, "TypeError", &world.type_error_proto, - crate::builtins::error::type_error_constructor as *const (), + oxide_builtins::error::type_error_constructor:: as *const (), ); bind_error_subtype_global( core, @@ -329,7 +414,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession global, "ReferenceError", &world.reference_error_proto, - crate::builtins::error::reference_error_constructor as *const (), + oxide_builtins::error::reference_error_constructor:: as *const (), ); bind_error_subtype_global( core, @@ -337,7 +422,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession global, "RangeError", &world.range_error_proto, - crate::builtins::error::range_error_constructor as *const (), + oxide_builtins::error::range_error_constructor:: as *const (), ); bind_error_subtype_global( core, @@ -345,7 +430,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession global, "SyntaxError", &world.syntax_error_proto, - crate::builtins::error::syntax_error_constructor as *const (), + oxide_builtins::error::syntax_error_constructor:: as *const (), ); bind_error_subtype_global( core, @@ -353,7 +438,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession global, "URIError", &world.uri_error_proto, - crate::builtins::error::uri_error_constructor as *const (), + oxide_builtins::error::uri_error_constructor:: as *const (), ); bind_error_subtype_global( core, @@ -361,7 +446,7 @@ pub fn bind_global_builtin_slots(core: &Arc, session: &KernelSession global, "EvalError", &world.eval_error_proto, - crate::builtins::error::eval_error_constructor as *const (), + oxide_builtins::error::eval_error_constructor:: as *const (), ); bind_reflect_global(core, session, global); diff --git a/crates/oxide_vm/src/builtins/error.rs b/crates/oxide_vm/src/builtins/error.rs deleted file mode 100644 index 2cb1296..0000000 --- a/crates/oxide_vm/src/builtins/error.rs +++ /dev/null @@ -1,194 +0,0 @@ -use std::sync::Arc; - -use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; -use oxide_types::mem::P; -use oxide_types::object::JsObject; -use oxide_types::value::JsValue; - -use crate::coercion; -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; - -fn create_error_object(vm: &mut Vm, error_proto_ptr: *mut JsObject, name: &str, message: &str) -> JsValue { - let obj = vm - .epoch() - .alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(error_proto_ptr))); - - let sf = Arc::clone(vm.kernel_core().perm_interner()); - let sh = Arc::clone(vm.kernel_core().shape_forge()); - - let si_message = sf.intern("message").0; - let sh_message = sh.make_shape(EMPTY_SHAPE_ID, si_message); - let msg_val = vm.new_string(message); - unsafe { - (*obj).set_shape_id(sh_message); - (*obj).push_prop(msg_val); - } - - let si_name = sf.intern("name").0; - let sh_name = sh.make_shape(sh_message, si_name); - let name_val = vm.new_string(name); - unsafe { - (*obj).set_shape_id(sh_name); - (*obj).push_prop(name_val); - } - - JsValue::from_js_object(obj) -} - -pub fn create_kind_error(vm: &mut Vm, kind: &str, msg: &str) -> JsValue { - let proto_ptr = match kind { - "TypeError" => P::as_ptr(&vm.session().builtin_world().type_error_proto) as *mut JsObject, - "RangeError" => P::as_ptr(&vm.session().builtin_world().range_error_proto) as *mut JsObject, - "ReferenceError" => P::as_ptr(&vm.session().builtin_world().reference_error_proto) as *mut JsObject, - "SyntaxError" => P::as_ptr(&vm.session().builtin_world().syntax_error_proto) as *mut JsObject, - "URIError" => P::as_ptr(&vm.session().builtin_world().uri_error_proto) as *mut JsObject, - "EvalError" => P::as_ptr(&vm.session().builtin_world().eval_error_proto) as *mut JsObject, - _ => P::as_ptr(&vm.session().builtin_world().error_proto) as *mut JsObject, - }; - create_error_object(vm, proto_ptr, kind, msg) -} - -pub fn create_type_error(vm: &mut Vm, msg: &str) -> JsValue { - create_kind_error(vm, "TypeError", msg) -} - -pub fn create_error(vm: &mut Vm, msg: &str) -> JsValue { - create_kind_error(vm, "Error", msg) -} - -pub fn create_reference_error(vm: &mut Vm, msg: &str) -> JsValue { - create_kind_error(vm, "ReferenceError", msg) -} - -pub fn create_range_error(vm: &mut Vm, msg: &str) -> JsValue { - create_kind_error(vm, "RangeError", msg) -} - -pub fn create_syntax_error(vm: &mut Vm, msg: &str) -> JsValue { - create_kind_error(vm, "SyntaxError", msg) -} - -pub fn create_uri_error(vm: &mut Vm, msg: &str) -> JsValue { - create_kind_error(vm, "URIError", msg) -} - -fn get_msg(vm: &mut Vm, args: &[u8]) -> String { - if args.len() > 1 { - coercion::to_string(vm.reg(args[1])) - } else { - String::new() - } -} - -pub fn error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let msg = get_msg(vm, args); - let proto_ptr = P::as_ptr(&vm.session().builtin_world().error_proto) as *mut JsObject; - NativeResult::Ok(create_error_object(vm, proto_ptr, "Error", &msg)) -} - -pub fn type_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let msg = get_msg(vm, args); - let proto_ptr = P::as_ptr(&vm.session().builtin_world().type_error_proto) as *mut JsObject; - NativeResult::Ok(create_error_object(vm, proto_ptr, "TypeError", &msg)) -} - -pub fn reference_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let msg = get_msg(vm, args); - let proto_ptr = P::as_ptr(&vm.session().builtin_world().reference_error_proto) as *mut JsObject; - NativeResult::Ok(create_error_object(vm, proto_ptr, "ReferenceError", &msg)) -} - -pub fn range_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let msg = get_msg(vm, args); - let proto_ptr = P::as_ptr(&vm.session().builtin_world().range_error_proto) as *mut JsObject; - NativeResult::Ok(create_error_object(vm, proto_ptr, "RangeError", &msg)) -} - -pub fn syntax_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let msg = get_msg(vm, args); - let proto_ptr = P::as_ptr(&vm.session().builtin_world().syntax_error_proto) as *mut JsObject; - NativeResult::Ok(create_error_object(vm, proto_ptr, "SyntaxError", &msg)) -} - -pub fn uri_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let msg = get_msg(vm, args); - let proto_ptr = P::as_ptr(&vm.session().builtin_world().uri_error_proto) as *mut JsObject; - NativeResult::Ok(create_error_object(vm, proto_ptr, "URIError", &msg)) -} - -pub fn eval_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult { - let msg = get_msg(vm, args); - let proto_ptr = P::as_ptr(&vm.session().builtin_world().eval_error_proto) as *mut JsObject; - NativeResult::Ok(create_error_object(vm, proto_ptr, "EvalError", &msg)) -} - -pub fn error_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult { - let this_val = vm.reg(args[0]); - let (name, msg) = if this_val.is_object() { - let obj = unsafe { &*this_val.as_js_object_ptr() }; - let name_val = obj - .hash_props_vec() - .and_then(|v| v.first()) - .copied() - .unwrap_or(JsValue::undefined()); - let msg_val = obj - .hash_props_vec() - .and_then(|v| v.get(1)) - .copied() - .unwrap_or(JsValue::undefined()); - ( - vm.lookup_str(name_val).unwrap_or_else(|| "Error".to_string()), - vm.lookup_str(msg_val).unwrap_or_default(), - ) - } else { - ("Error".to_string(), String::new()) - }; - let result = crate::vm::format_error_message(&name, &msg); - NativeResult::Ok(vm.new_string(&result)) -} - -pub fn error_stack_getter(vm: &mut Vm, _args: &[u8]) -> NativeResult { - let mut lines = vec![]; - for frame in vm.frames.iter().rev() { - let func_name = vm - .kernel_core() - .perm_interner() - .lookup(frame.function_name) - .unwrap_or(""); - lines.push(format!(" at {} (native)", func_name)); - } - lines.push(" at (native)".to_string()); - let result = lines.join("\n"); - NativeResult::Ok(vm.new_string(&result)) -} - -#[cfg(test)] -mod tests { - use super::error_stack_getter; - use crate::vm::{CallFrame, FrameContinuation, Vm}; - use oxide_types::value::JsValue; - - #[test] - fn error_stack_uses_call_frame_function_name() { - let mut vm = Vm::new(); - let name_si = vm.kernel_core().perm_interner().intern("foo").0; - vm.frames.push(CallFrame { - return_addr: 0, - function_name: name_si, - caller_reg_limit: 1, - saved_reg_offset: 0, - saved_this: JsValue::undefined(), - saved_new_target: JsValue::undefined(), - callee: JsValue::undefined(), - construct_result_reg: None, - constructed_this: None, - is_derived_constructor: false, - continuation: FrameContinuation::None, - }); - - let stack = error_stack_getter(&mut vm, &[0]).unwrap(); - let stack_str = vm.lookup_str(stack).unwrap_or_default(); - assert!(stack_str.contains("foo"), "expected function name in stack: {stack_str}"); - } -} diff --git a/crates/oxide_vm/src/builtins/stubs.rs b/crates/oxide_vm/src/builtins/stubs.rs deleted file mode 100644 index 02307a8..0000000 --- a/crates/oxide_vm/src/builtins/stubs.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::vm::Vm; -use oxide_runtime_api::NativeResult; - -fn stub_error(vm: &mut Vm, name: &str) -> NativeResult { - NativeResult::Err(crate::builtins::error::create_type_error(vm, &format!("{name} is not implemented"))) -} - -// Architecturally deferred — these features are excluded from the supported language subset by design. -pub fn proxy_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "Proxy") -} -pub fn bigint_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "BigInt") -} -pub fn weakmap_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "WeakMap") -} -pub fn weakset_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "WeakSet") -} -pub fn weakref_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "WeakRef") -} -pub fn finalization_registry_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "FinalizationRegistry") -} -pub fn shared_array_buffer_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "SharedArrayBuffer") -} -pub fn atomics_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult { - stub_error(vm, "Atomics") -} diff --git a/crates/oxide_vm/src/coercion.rs b/crates/oxide_vm/src/coercion.rs deleted file mode 100644 index 5c26716..0000000 --- a/crates/oxide_vm/src/coercion.rs +++ /dev/null @@ -1,343 +0,0 @@ -use crate::vm::Vm; -use oxide_types::object::JsObject; -use oxide_types::shape::EMPTY_SHAPE_ID; -use oxide_types::value::JsValue; - -/// Borrow a string value's content. Caller must hold a valid string `JsValue`. -/// -/// # Safety -/// `val` must be a string `JsValue` whose `JsString` pointer is alive (session or -/// permanent string) — the same contract as object-pointer derefs in the VM. -#[inline] -unsafe fn string_data(val: JsValue) -> &'static str { - (*val.as_string_ptr()).as_str() -} - -/// Content equality of two string values: pointer identity → hash+len reject → -/// byte compare. Replaces the old interner-index compare. -#[inline] -fn string_value_eq(a: JsValue, b: JsValue) -> bool { - if a.as_string_ptr() == b.as_string_ptr() { - return true; - } - // SAFETY: both are string JsValues; their JsString pointers are valid. - let sa = unsafe { &*a.as_string_ptr() }; - let sb = unsafe { &*b.as_string_ptr() }; - sa.hash == sb.hash && sa.data == sb.data -} - -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_string() { - // SAFETY: val is a string value. - let s = unsafe { string_data(val) }; - return s.parse::().unwrap_or(f64::NAN); - } - if val.is_object() { - return f64::NAN; - } - f64::NAN -} - -pub fn to_uint32(val: JsValue) -> u32 { - let n = to_number(val); - if n == 0.0 || !n.is_finite() { - return 0; - } - n.trunc().rem_euclid(4_294_967_296.0) as u32 -} - -pub fn to_int32(val: JsValue) -> i32 { - let int = to_uint32(val); - if int > i32::MAX as u32 { - (int as i64 - 4_294_967_296i64) as i32 - } else { - int as i32 - } -} - -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() - }; - } - if d.is_finite() && d.fract() == 0.0 { - return (d as i64).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_string() { - // SAFETY: val is a string value. - return unsafe { string_data(val) }.to_string(); - } - if val.is_object() { - return "[object]".to_string(); - } - 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_string() { - // SAFETY: val is a string value. - return !unsafe { (*val.as_string_ptr()).is_empty() }; - } - if val.is_object() { - return true; - } - if val.is_symbol() { - 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_string() && rhs.is_string() { - return string_value_eq(lhs, rhs); - } - 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() { - return lhs.is_object() && rhs.is_object() && lhs.as_ptr() == rhs.as_ptr(); - } - 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_string() && rhs.is_string() { - return string_value_eq(lhs, rhs); - } - if lhs.is_object() && rhs.is_object() { - return lhs.as_ptr() == rhs.as_ptr(); - } - if lhs.is_symbol() && rhs.is_symbol() { - return lhs.as_symbol_index() == rhs.as_symbol_index(); - } - 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 { - if lhs.is_string() && rhs.is_string() { - // SAFETY: both are string values. - let ls = unsafe { string_data(lhs) }; - let rs = unsafe { string_data(rhs) }; - return Some(ls < rs); - } - 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 to_f64(val: JsValue) -> f64 { - if val.is_int() { - val.as_int() as f64 - } else if val.is_double() { - val.as_double() - } else { - f64::NAN - } -} - -pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool { - if lhs.is_double() && rhs.is_double() { - let a = lhs.as_double(); - let b = rhs.as_double(); - if a.is_nan() && b.is_nan() { - return true; - } - if a == 0.0 && b == 0.0 { - let a_neg = a.is_sign_negative(); - let b_neg = b.is_sign_negative(); - return a_neg == b_neg; - } - return a == b; - } - if lhs.is_int() && rhs.is_int() { - return lhs.as_int() == rhs.as_int(); - } - if (lhs.is_int() || lhs.is_double()) && (rhs.is_int() || rhs.is_double()) { - let a = to_f64(lhs); - let b = to_f64(rhs); - if a.is_nan() && b.is_nan() { - return true; - } - if a == 0.0 && b == 0.0 { - return a.is_sign_negative() == b.is_sign_negative(); - } - return a == b; - } - 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_string() && rhs.is_string() { - return string_value_eq(lhs, rhs); - } - if lhs.is_object() && rhs.is_object() { - return lhs.as_ptr() == rhs.as_ptr(); - } - if lhs.is_symbol() && rhs.is_symbol() { - return lhs.as_symbol_index() == rhs.as_symbol_index(); - } - false -} - -pub fn strict_equality(lhs: JsValue, rhs: JsValue) -> bool { - if lhs.is_double() && rhs.is_double() { - return strict_double_eq(lhs.as_double(), rhs.as_double()); - } - same_value(lhs, rhs) -} - -pub fn to_object(val: JsValue, vm: &mut Vm) -> Result { - if val.is_object() { - return Ok(val); - } - if val.is_null() || val.is_undefined() { - return Err(vm.error_message_text("TypeError", "Cannot convert null or undefined to object")); - } - let (proto_ptr, type_tag) = if val.is_string() { - ( - vm.session().builtin_world().string_proto.as_ptr() as *mut JsObject, - JsObject::OBJ_TYPE_STRING_OBJ, - ) - } else if val.is_int() || val.is_double() { - ( - vm.session().builtin_world().number_proto.as_ptr() as *mut JsObject, - JsObject::OBJ_TYPE_NUMBER_OBJ, - ) - } else if val.is_bool() { - ( - vm.session().builtin_world().boolean_proto.as_ptr() as *mut JsObject, - JsObject::OBJ_TYPE_BOOLEAN_OBJ, - ) - } else { - (&*vm.object_prototype as *const JsObject as *mut JsObject, JsObject::OBJ_TYPE_PLAIN) - }; - let proto_val = JsValue::from_js_object(proto_ptr); - let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val)); - let obj_val = JsValue::from_js_object(obj); - let obj_ref = unsafe { &mut *obj }; - obj_ref.type_tag = type_tag; - obj_ref.ensure_hash_props().push(val); - obj_ref.set_prop_count(1); - Ok(obj_val) -} diff --git a/crates/oxide_vm/src/dispatch/arithmetic.rs b/crates/oxide_vm/src/dispatch/arithmetic.rs index 2c5c0e5..397c1ff 100644 --- a/crates/oxide_vm/src/dispatch/arithmetic.rs +++ b/crates/oxide_vm/src/dispatch/arithmetic.rs @@ -1,5 +1,5 @@ -use crate::coercion; use crate::vm::Vm; +use oxide_runtime_api as coercion; use oxide_types::value::JsValue; impl Vm { diff --git a/crates/oxide_vm/src/dispatch/call.rs b/crates/oxide_vm/src/dispatch/call.rs index 3d97469..bff0e10 100644 --- a/crates/oxide_vm/src/dispatch/call.rs +++ b/crates/oxide_vm/src/dispatch/call.rs @@ -54,7 +54,7 @@ impl Vm { format!("{err_val}") }; builtins_debug!("native_call err={}", msg); - (crate::builtins::error::create_error(self, &msg), "Error") + (oxide_builtins::error::create_error(self, &msg), "Error") }; self.exception_value = Some(error); self.pending_error_kind = Some(kind); diff --git a/crates/oxide_vm/src/dispatch/comparison.rs b/crates/oxide_vm/src/dispatch/comparison.rs index 7180e09..ac636e4 100644 --- a/crates/oxide_vm/src/dispatch/comparison.rs +++ b/crates/oxide_vm/src/dispatch/comparison.rs @@ -1,5 +1,5 @@ -use crate::coercion; use crate::vm::Vm; +use oxide_runtime_api as coercion; use oxide_types::value::JsValue; impl Vm { diff --git a/crates/oxide_vm/src/dispatch/control_flow.rs b/crates/oxide_vm/src/dispatch/control_flow.rs index 0003046..dce8e4c 100644 --- a/crates/oxide_vm/src/dispatch/control_flow.rs +++ b/crates/oxide_vm/src/dispatch/control_flow.rs @@ -1,6 +1,6 @@ -use crate::coercion; use crate::vm::Vm; use oxide_bytecode::opcode::{self, Instr}; +use oxide_runtime_api as coercion; use oxide_types::value::JsValue; impl Vm { diff --git a/crates/oxide_vm/src/dispatch/member.rs b/crates/oxide_vm/src/dispatch/member.rs index 06931fb..871b8e5 100644 --- a/crates/oxide_vm/src/dispatch/member.rs +++ b/crates/oxide_vm/src/dispatch/member.rs @@ -2,8 +2,8 @@ use oxide_bytecode::opcode::OpCode; use oxide_types::object::JsObject; use oxide_types::value::JsValue; -use crate::coercion; use crate::vm::Vm; +use oxide_runtime_api as coercion; impl Vm { pub(crate) fn dispatch_member_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> { diff --git a/crates/oxide_vm/src/lib.rs b/crates/oxide_vm/src/lib.rs index 9ba9381..5fc3224 100644 --- a/crates/oxide_vm/src/lib.rs +++ b/crates/oxide_vm/src/lib.rs @@ -1,8 +1,6 @@ #![doc = "OxideJS - Register-based VM with epoch arena memory"] pub mod bindings; -pub mod builtins; -pub mod coercion; mod dispatch; mod ic_helper; pub mod native; diff --git a/crates/oxide_vm/src/session_arena.rs b/crates/oxide_vm/src/session_arena.rs index 15aaa60..b02a19b 100644 --- a/crates/oxide_vm/src/session_arena.rs +++ b/crates/oxide_vm/src/session_arena.rs @@ -4,8 +4,8 @@ use oxide_types::object::JsObject; use oxide_types::value::JsValue; use rustc_hash::FxBuildHasher; -use crate::builtins::{data_view, map, set, typed_array}; use crate::vm::Vm; +use oxide_builtins::{data_view, map, set, typed_array}; impl Vm { pub(crate) fn is_session_escape_root_ptr(&self, target_ptr: *mut JsObject) -> bool { diff --git a/crates/oxide_vm/src/session_gc.rs b/crates/oxide_vm/src/session_gc.rs index 79e1001..e2bf5b2 100644 --- a/crates/oxide_vm/src/session_gc.rs +++ b/crates/oxide_vm/src/session_gc.rs @@ -7,8 +7,8 @@ use oxide_types::object::{JsObject, JsString, PropMetaEntry}; use oxide_types::value::JsValue; use rustc_hash::FxBuildHasher; -use crate::builtins::{array_buffer, data_view, map, regexp, set, typed_array}; use crate::vm::Vm; +use oxide_builtins::{array_buffer, data_view, map, regexp, set, typed_array}; pub struct SessionGc { pub total_collections: u64, @@ -502,8 +502,8 @@ mod tests { use oxide_types::object::JsObject; use super::*; - use crate::builtins::{array_buffer, data_view, map, set, typed_array}; use crate::vm::{CallFrame, FrameContinuation}; + use oxide_builtins::{array_buffer, data_view, map, set, typed_array}; use oxide_runtime_api::NativeResult; fn plain_object(vm: &mut Vm) -> *mut JsObject { diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index 140e00e..cc45439 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -7,15 +7,15 @@ use oxide_bytecode::opcode::{self, OpCode}; use smallvec::SmallVec; pub use crate::bindings::init_kernel_builtins; -use crate::coercion; use crate::native::NativeFn; use crate::session_gc::SessionGc; use crate::vm_state::{GcState, IterState, ProfilingState, SymbolState}; use oxide_kernel::kernel::{KernelCore, KernelSession}; +use oxide_runtime_api as coercion; use oxide_runtime_api::NativeResult; use oxide_types::error::{JsError, JsErrorKind}; use oxide_types::mem::{Epoch, P}; -use oxide_types::object::{JsObject, NativeFnPtr}; +use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; pub(crate) const MAX_PROTO_CHAIN_DEPTH: usize = 1024; @@ -408,7 +408,7 @@ impl Vm { pub(crate) fn raise_js_error(&mut self, err: JsError) -> Result<(), String> { let kind = js_error_kind_name(err.kind); - let error = crate::builtins::error::create_kind_error(self, kind, &err.message); + let error = oxide_builtins::error::create_kind_error(self, kind, &err.message); self.exception_value = Some(error); self.pending_error_kind = Some(kind); self.unwind() @@ -1457,3 +1457,121 @@ mod tests { assert_eq!(vm.reg(255), JsValue::int(13)); } } + +impl oxide_runtime_api::VmHost for Vm { + fn reg(&self, idx: u8) -> JsValue { + self.reg(idx) + } + fn set_reg(&mut self, idx: u8, val: JsValue) { + self.set_reg(idx, val); + } + fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject { + self.alloc_object(obj) + } + fn new_string(&mut self, s: &str) -> JsValue { + self.new_string(s) + } + fn kernel_core(&self) -> &Arc { + self.kernel_core() + } + fn session(&self) -> &KernelSession { + self.session() + } + fn epoch(&self) -> &Epoch { + self.epoch() + } + fn property_key_si(&mut self, val: JsValue) -> u32 { + self.property_key_si(val) + } + fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option { + self.resolve_property(obj, prop_name_si) + } + fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option { + self.get_own_property_slot(obj, prop_name_si) + } + fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result { + self.ordinary_get(obj, prop_name_si, receiver) + } + fn ordinary_set( + &mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue, + ) -> Result<(), String> { + self.ordinary_set(obj, prop_name_si, val, receiver) + } + fn define_data_property( + &mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes, + ) -> Result<(), String> { + self.define_data_property(obj, prop_name_si, val, attributes) + } + fn define_accessor_property( + &mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes, + ) -> Result<(), String> { + self.define_accessor_property(obj, prop_name_si, get, set, attributes) + } + fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) { + self.set_or_create_prop_value(obj, prop_name_si, val) + } + fn lookup_str(&self, val: JsValue) -> Option { + self.lookup_str(val) + } + fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result { + self.coerce_primitive_bounded(value, prefer_string) + } + fn coerce_number_bounded(&mut self, value: JsValue) -> Result { + self.coerce_number_bounded(value) + } + fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result { + self.call_function_sync(callee, receiver, args) + } + fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result, String> { + self.checked_object_ptr(val, error_msg) + } + fn raise_type_error(&mut self, msg: &str) -> Result<(), String> { + self.raise_type_error(msg) + } + fn error_message_text(&self, kind: &str, msg: &str) -> String { + self.error_message_text(kind, msg) + } + fn call_stack_function_names(&self) -> Vec { + self.frames + .iter() + .rev() + .map(|f| { + self.kernel_core + .perm_interner() + .lookup(f.function_name) + .unwrap_or("") + .to_string() + }) + .collect() + } + fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue { + self.promote_if_needed_for_write_ptr(target_ptr, value) + } + fn step_rng(&mut self) { + self.step_rng() + } + fn math_rng_value(&self) -> f64 { + self.math_rng_value() + } + fn sub_module_function_name(&self, sub_idx: u16) -> String { + self.sub_modules + .get(sub_idx as usize) + .and_then(|m| m.function_name.clone()) + .unwrap_or_default() + } + fn symbol_intern(&mut self, desc: String) -> u32 { + self.symbols.intern(desc) + } + fn symbol_description(&self, idx: u32) -> Option<&str> { + self.symbols.description(idx) + } + fn symbol_lookup_global(&self, key: &str) -> Option { + self.symbols.lookup_global(key) + } + fn symbol_register_global(&mut self, key: String, idx: u32) { + self.symbols.register_global(key, idx) + } + fn symbol_key_for_id(&self, idx: u32) -> Option { + self.symbols.key_for_id(idx) + } +} diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index c32e2d7..9353d90 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use crate::native::NativeFn; use crate::vm::{native_fn_ptr_to_fn, CallFrame, ForInIter, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; -use oxide_runtime_api::NativeResult; +use oxide_runtime_api::{to_boolean, NativeResult}; use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::private_key::is_private_name_key; use oxide_types::value::JsValue; @@ -155,7 +155,7 @@ impl Vm { Ok(true) } else { let error = - crate::builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported"); + oxide_builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported"); self.exception_value = Some(error); self.pending_error_kind = Some(self.thrown_error_kind(error)); self.unwind().map(|_| true) @@ -344,7 +344,7 @@ impl Vm { pub(crate) fn dispatch_for_of_init(&mut self, a: usize) -> Result<(), String> { let iterable = self.regs[a]; - match crate::builtins::iterator::make_iterator_for_value(self, iterable) { + match oxide_builtins::iterator::make_iterator_for_value(self, iterable) { Ok(iterator) => { self.iters.push_for_of(iterator); self.iters.clear_last_for_of_result(); @@ -377,7 +377,7 @@ impl Vm { let result_obj = unsafe { &*result.as_js_object_ptr() }; let done_si = self.kernel_core.perm_interner().intern("done").0; let done_val = self.ordinary_get(result_obj, done_si, result)?; - let done = crate::coercion::to_boolean(done_val); + let done = to_boolean(done_val); self.iters.set_last_for_of_result(result); self.regs[rd] = JsValue::bool(!done); Ok(()) diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index 13f3e01..1ea5173 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -1,5 +1,5 @@ -use crate::coercion; use crate::vm::{FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH}; +use oxide_runtime_api as coercion; use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry}; use oxide_types::value::JsValue; diff --git a/crates/oxide_vm/tests/builtins_array_tests.rs b/crates/oxide_vm/tests/builtins_array_tests.rs index 7882211..82cb780 100644 --- a/crates/oxide_vm/tests/builtins_array_tests.rs +++ b/crates/oxide_vm/tests/builtins_array_tests.rs @@ -1,6 +1,6 @@ +use oxide_builtins::array::{array_constructor, array_push}; use oxide_compiler::compiler::Compiler; use oxide_types::value::JsValue; -use oxide_vm::builtins::array::{array_constructor, array_push}; use oxide_vm::vm::Vm; fn eval(source: &str) -> Result<(Vm, JsValue), String> { diff --git a/crates/oxide_vm/tests/builtins_boolean_tests.rs b/crates/oxide_vm/tests/builtins_boolean_tests.rs index b11684f..958455a 100644 --- a/crates/oxide_vm/tests/builtins_boolean_tests.rs +++ b/crates/oxide_vm/tests/builtins_boolean_tests.rs @@ -1,7 +1,7 @@ +use oxide_builtins::boolean::{boolean_constructor, boolean_prototype_to_string, boolean_prototype_value_of}; use oxide_compiler::compiler::Compiler; 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; diff --git a/crates/oxide_vm/tests/builtins_error_tests.rs b/crates/oxide_vm/tests/builtins_error_tests.rs index 47a4eb1..58eca22 100644 --- a/crates/oxide_vm/tests/builtins_error_tests.rs +++ b/crates/oxide_vm/tests/builtins_error_tests.rs @@ -1,6 +1,6 @@ +use oxide_builtins::error; use oxide_compiler::compiler::Compiler; use oxide_types::value::JsValue; -use oxide_vm::builtins::error; use oxide_vm::vm::Vm; fn make_vm() -> Vm { diff --git a/crates/oxide_vm/tests/builtins_map_tests.rs b/crates/oxide_vm/tests/builtins_map_tests.rs index ad3ee11..d4642e4 100644 --- a/crates/oxide_vm/tests/builtins_map_tests.rs +++ b/crates/oxide_vm/tests/builtins_map_tests.rs @@ -1,6 +1,6 @@ +use oxide_builtins::map::{map_clear, map_constructor as new_map, map_delete, map_get, map_has, map_set, map_size}; use oxide_compiler::compiler::Compiler; 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; fn eval(vm: &mut Vm, source: &str) -> Result { diff --git a/crates/oxide_vm/tests/builtins_set_tests.rs b/crates/oxide_vm/tests/builtins_set_tests.rs index db4606a..8c6a210 100644 --- a/crates/oxide_vm/tests/builtins_set_tests.rs +++ b/crates/oxide_vm/tests/builtins_set_tests.rs @@ -1,6 +1,6 @@ +use oxide_builtins::set::{set_add, set_clear, set_constructor as new_set, set_delete, set_has, set_size}; use oxide_compiler::compiler::Compiler; 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; fn eval(vm: &mut Vm, source: &str) -> Result { diff --git a/crates/oxide_vm/tests/coercion_tests.rs b/crates/oxide_vm/tests/coercion_tests.rs index 28a24bb..588cc72 100644 --- a/crates/oxide_vm/tests/coercion_tests.rs +++ b/crates/oxide_vm/tests/coercion_tests.rs @@ -1,7 +1,7 @@ use oxide_compiler::compiler::Compiler; use oxide_parser::Allocator; +use oxide_runtime_api as coercion; use oxide_types::value::JsValue; -use oxide_vm::coercion; use oxide_vm::vm::Vm; fn eval(source: &str) -> String {