From a032a0a0fea135cf55ec1005058ed044c6220171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Fri, 26 Jun 2026 21:24:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=BA=20Array/String=20=E5=8E=9F?= =?UTF-8?q?=E5=9E=8B=E6=96=B9=E6=B3=95=E6=B7=BB=E5=8A=A0=E5=85=A5=E5=8F=A3?= =?UTF-8?q?=E5=92=8C=E9=94=99=E8=AF=AF=E8=B7=AF=E5=BE=84=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E5=8C=96=E6=97=A5=E5=BF=97=EF=BC=8C=E7=BC=96=E8=AF=91=E5=99=A8?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=98=B6=E6=AE=B5=E8=BE=B9=E7=95=8C=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/array.rs | 226 ++++++++++++++++++++++---- crates/oxide_builtins/src/string.rs | 34 ++++ crates/oxide_compiler/src/compiler.rs | 5 + 3 files changed, 229 insertions(+), 36 deletions(-) diff --git a/crates/oxide_builtins/src/array.rs b/crates/oxide_builtins/src/array.rs index a4bf74d..66c123e 100644 --- a/crates/oxide_builtins/src/array.rs +++ b/crates/oxide_builtins/src/array.rs @@ -4,11 +4,17 @@ use oxide_types::value::JsValue; use oxide_runtime_api::{NativeResult, VmHost}; +use crate::builtins_debug; +use crate::builtins_error; + macro_rules! array_ptr { ($vm:expr, $args:expr) => {{ match get_this_array_ref($vm, $vm.reg($args[0])) { Ok(ptr) => ptr, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array method: invalid receiver"); + return NativeResult::Err(err); + } } }}; } @@ -158,6 +164,7 @@ pub fn array_is_array(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_push(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.push called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let mut len = unsafe { &*arr_ptr }.prop_count(); for &arg_reg in args.iter().skip(1) { @@ -170,6 +177,7 @@ pub fn array_push(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_pop(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.pop called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count(); @@ -182,6 +190,7 @@ pub fn array_pop(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_slice(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.slice called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -210,6 +219,7 @@ pub fn array_slice(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_splice(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.splice called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let n = arr.prop_count() as usize; @@ -272,6 +282,7 @@ pub fn array_splice(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_concat(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.concat called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -307,6 +318,7 @@ pub fn array_concat(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_join(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.join called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -331,6 +343,7 @@ pub fn array_join(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_index_of(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.indexOf called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -347,6 +360,7 @@ pub fn array_index_of(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_includes(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.includes called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -371,6 +385,7 @@ pub fn array_includes(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_reverse(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.reverse called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let n = arr.prop_count() as usize; @@ -387,6 +402,7 @@ pub fn array_reverse(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_flat(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.flat called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let n = arr.prop_count() as usize; @@ -443,34 +459,50 @@ pub fn array_flat(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_for_each(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.forEach called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.forEach: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.forEach: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; for i in 0..n { 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(_) => {} - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.forEach: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.forEach: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(JsValue::undefined()) } pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.map called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.map: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.map: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; let new_arr = create_new_array(vm, n); @@ -480,8 +512,14 @@ pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(mapped) => unsafe { (*new_arr).set_prop_at(i, mapped); }, - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.map: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.map: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } unsafe { @@ -491,13 +529,18 @@ pub fn array_map(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.filter called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.filter: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.filter: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; let mut kept: Vec = Vec::new(); @@ -509,8 +552,14 @@ pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { kept.push(elem); } } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.filter: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.filter: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } let new_arr = create_new_array(vm, kept.len()); @@ -524,16 +573,22 @@ pub fn array_filter(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.reduce called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if n == 0 && args.len() < 3 { + builtins_error!("Array.prototype.reduce: invalid receiver"); return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value")); } if args.len() < 2 { + builtins_error!("Array.prototype.reduce: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return NativeResult::Err(err); + } }; let has_initial = args.len() > 2; let mut accumulator; @@ -555,21 +610,32 @@ pub fn array_reduce(vm: &mut H, args: &[u8]) -> NativeResult { &[accumulator, elem, JsValue::int(i as i32), vm.reg(args[0])], ) { NativeResult::Ok(result) => accumulator = result, - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.reduce: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(accumulator) } pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.find called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.find: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.find: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; for i in 0..n { @@ -580,21 +646,32 @@ pub fn array_find(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Ok(elem); } } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.find: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.find: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(JsValue::undefined()) } pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.some called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.some: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.some: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; for i in 0..n { @@ -605,21 +682,32 @@ pub fn array_some(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Ok(JsValue::bool(true)); } } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.some: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.some: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(JsValue::bool(false)) } pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.every called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.every: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.every: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; for i in 0..n { @@ -630,21 +718,32 @@ pub fn array_every(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Ok(JsValue::bool(false)); } } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.every: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.every: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(JsValue::bool(true)) } pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.flatMap called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.flatMap: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.flatMap: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; let mut flat: Vec = Vec::new(); @@ -667,8 +766,14 @@ pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { } flat.push(result); } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.flatMap: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.flatMap: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } let new_arr = create_new_array(vm, flat.len()); @@ -682,6 +787,7 @@ pub fn array_flat_map(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_shift(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.shift called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count(); @@ -698,6 +804,7 @@ pub fn array_shift(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_unshift(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.unshift called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count(); @@ -715,6 +822,7 @@ pub fn array_unshift(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_fill(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.fill called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count() as usize; @@ -737,6 +845,7 @@ pub fn array_fill(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_copy_within(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.copyWithin called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &mut *arr_ptr }; let len = arr.prop_count() as usize; @@ -771,6 +880,7 @@ pub fn array_copy_within(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_at(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.at called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let len = arr.prop_count() as i32; @@ -789,6 +899,7 @@ pub fn array_at(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_last_index_of(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.lastIndexOf called with {} args", args.len()); let arr_ptr = array_ptr!(vm, args); let arr = unsafe { &*arr_ptr }; let len = arr.prop_count() as i32; @@ -802,13 +913,18 @@ pub fn array_last_index_of(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.findIndex called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.findIndex: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.findIndex: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; for i in 0..n { @@ -819,24 +935,35 @@ pub fn array_find_index(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Ok(JsValue::int(i as i32)); } } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.findIndex: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.findIndex: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(JsValue::int(-1)) } pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.findLast called with {} args", args.len()); let (arr_ptr, n) = { let (arr_ptr, len) = array_ptr_len!(vm, args); (arr_ptr, len as i32) }; if args.len() < 2 { + builtins_error!("Array.prototype.findLast: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.findLast: invalid receiver"); + return NativeResult::Err(err); + } }; let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() }; for i in (0..n).rev() { @@ -847,26 +974,38 @@ pub fn array_find_last(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Ok(elem); } } - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.findLast: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.findLast: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(JsValue::undefined()) } pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.reduceRight called with {} args", args.len()); let (arr_ptr, n) = array_ptr_len!(vm, args); if args.len() < 2 { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); return NativeResult::Err(array_type_error(vm, "callback is not a function")); } let callback_val = match require_callback(vm, vm.reg(args[1])) { Ok(callback) => callback, - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return NativeResult::Err(err); + } }; let (mut acc, start_idx): (JsValue, i32) = if args.len() > 2 { (vm.reg(args[2]), n as i32 - 1) } else { if n == 0 { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value")); } (unsafe { (*arr_ptr).get_prop_at(n - 1) }, n as i32 - 2) @@ -880,14 +1019,21 @@ pub fn array_reduce_right(vm: &mut H, args: &[u8]) -> NativeResult { &[acc, elem, JsValue::int(i), vm.reg(args[0])], ) { NativeResult::Ok(r) => acc = r, - NativeResult::Err(err) => return NativeResult::Err(err), - NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm), + NativeResult::Err(err) => { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return NativeResult::Err(err); + } + NativeResult::TailCall { .. } => { + builtins_error!("Array.prototype.reduceRight: invalid receiver"); + return unexpected_tail_call_error(vm); + } } } NativeResult::Ok(acc) } pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.sort called with {} args", args.len()); 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(); @@ -898,7 +1044,10 @@ pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { } else { match require_callback(vm, candidate) { Ok(callback) => Some(callback), - Err(err) => return NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.sort: invalid receiver"); + return NativeResult::Err(err); + } } } } else { @@ -937,6 +1086,7 @@ pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { } }); if let Some(err) = sort_error { + builtins_error!("Array.prototype.sort: invalid receiver"); return NativeResult::Err(err); } let arr = unsafe { &mut *arr_ptr }; @@ -947,9 +1097,13 @@ pub fn array_sort(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn array_values(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("Array.prototype.values called with {} args", args.len()); let this_val = vm.reg(args[0]); match crate::iterator::make_iterator_for_value(vm, this_val) { Ok(iterator) => NativeResult::Ok(iterator), - Err(err) => NativeResult::Err(err), + Err(err) => { + builtins_error!("Array.prototype.values: invalid receiver"); + NativeResult::Err(err) + } } } diff --git a/crates/oxide_builtins/src/string.rs b/crates/oxide_builtins/src/string.rs index 685a420..8d98349 100644 --- a/crates/oxide_builtins/src/string.rs +++ b/crates/oxide_builtins/src/string.rs @@ -5,11 +5,15 @@ use oxide_types::value::JsValue; use memchr::memchr; use oxide_runtime_api::{NativeResult, VmHost}; +use crate::builtins_debug; +use crate::builtins_error; + 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 H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.fromCharCode called with {} args", args.len()); let mut out = String::new(); for &arg_reg in args.iter().skip(1) { let code = oxide_runtime_api::to_uint32(vm.reg(arg_reg)) & 0xFFFF; @@ -23,6 +27,7 @@ pub fn string_from_char_code(vm: &mut H, args: &[u8]) -> NativeResult } pub fn string_value_of(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.valueOf called with {} args", args.len()); let this_val = vm.reg(args[0]); if this_val.is_string() { return NativeResult::Ok(this_val); @@ -36,6 +41,7 @@ pub fn string_value_of(vm: &mut H, args: &[u8]) -> NativeResult { } } } + builtins_error!("String.prototype.valueOf: invalid receiver"); NativeResult::Err(crate::error::create_type_error( vm, "String.prototype.valueOf called on non-String object", @@ -157,6 +163,7 @@ fn is_regexp_obj(val: JsValue, vm: &H) -> bool { } pub fn string_index_of(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.indexOf called with {} args", args.len()); let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -188,6 +195,7 @@ pub fn string_index_of(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_includes(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.includes called with {} args", args.len()); let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -213,6 +221,7 @@ pub fn string_includes(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_char_at(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.charAt called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { if s.is_empty() { @@ -229,6 +238,7 @@ pub fn string_char_at(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_char_code_at(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.charCodeAt called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { if s.is_empty() { @@ -244,6 +254,7 @@ pub fn string_char_code_at(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_concat(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.concat called with {} args", args.len()); let mut result = this_string(vm, args); for &arg_reg in args.iter().skip(1) { result.push_str(&oxide_runtime_api::to_string(vm.reg(arg_reg))); @@ -252,6 +263,7 @@ pub fn string_concat(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_slice(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.slice called with {} args", args.len()); let s = this_string(vm, args); let n = char_len(&s) as i32; let start = if args.len() > 1 { @@ -281,6 +293,7 @@ pub fn string_slice(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_substring(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.substring called with {} args", args.len()); let s = this_string(vm, args); let n = char_len(&s) as i32; let mut start = if args.len() > 1 { @@ -301,21 +314,25 @@ pub fn string_substring(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_to_upper_case(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.toUpperCase called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(&s.to_uppercase())) } pub fn string_to_lower_case(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.toLowerCase called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(&s.to_lowercase())) } pub fn string_trim(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.trim called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim())) } pub fn string_repeat(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.repeat called with {} args", args.len()); let s = this_string(vm, args); let n = if args.len() > 1 { (oxide_runtime_api::to_number(vm.reg(args[1])) as usize).min(10000) @@ -326,6 +343,7 @@ pub fn string_repeat(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_pad_start(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.padStart called with {} args", args.len()); let s = this_string(vm, args); let s_len = char_len(&s); let target = if args.len() > 1 { @@ -334,6 +352,7 @@ pub fn string_pad_start(vm: &mut H, args: &[u8]) -> NativeResult { s_len }; if target > 10000 { + builtins_error!("String.prototype.padStart: invalid receiver"); 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() }; @@ -349,6 +368,7 @@ pub fn string_pad_start(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_pad_end(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.padEnd called with {} args", args.len()); let s = this_string(vm, args); let s_len = char_len(&s); let target = if args.len() > 1 { @@ -357,6 +377,7 @@ pub fn string_pad_end(vm: &mut H, args: &[u8]) -> NativeResult { s_len }; if target > 10000 { + builtins_error!("String.prototype.padEnd: invalid receiver"); 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() }; @@ -372,6 +393,7 @@ pub fn string_pad_end(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_starts_with(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.startsWith called with {} args", args.len()); let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -387,6 +409,7 @@ pub fn string_starts_with(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_ends_with(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.endsWith called with {} args", args.len()); let s = this_string(vm, args); let n = char_len(&s); if args.len() < 2 { @@ -402,6 +425,7 @@ pub fn string_ends_with(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_split(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.split called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { let parts = vec![s.clone()]; @@ -441,6 +465,7 @@ pub fn string_split(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.replace called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(vm.new_string(&s)); @@ -467,6 +492,7 @@ pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_match_fn(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.match called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(JsValue::undefined()); @@ -494,6 +520,7 @@ pub fn string_match_fn(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_search(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.search called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(JsValue::int(-1)); @@ -523,16 +550,19 @@ pub fn string_search(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_trim_start(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.trimStart called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim_start())) } pub fn string_trim_end(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.trimEnd called with {} args", args.len()); let s = this_string(vm, args); NativeResult::Ok(vm.new_string(s.trim_end())) } pub fn string_code_point_at(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.codePointAt called with {} args", args.len()); let s = this_string(vm, args); let pos = if args.len() > 1 { oxide_runtime_api::to_number(vm.reg(args[1])) as usize @@ -555,6 +585,7 @@ pub fn string_code_point_at(vm: &mut H, args: &[u8]) -> NativeResult } pub fn string_normalize(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.normalize called with {} args", args.len()); 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() }; @@ -568,8 +599,10 @@ pub fn string_normalize(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_match_all(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.matchAll called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { + builtins_error!("String.prototype.matchAll: invalid receiver"); return NativeResult::Err(JsValue::undefined()); } let pattern_val = vm.reg(args[1]); @@ -589,6 +622,7 @@ pub fn string_match_all(vm: &mut H, args: &[u8]) -> NativeResult { } pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { + builtins_debug!("String.prototype.replaceAll called with {} args", args.len()); let s = this_string(vm, args); if args.len() < 2 { return NativeResult::Ok(vm.new_string(&s)); diff --git a/crates/oxide_compiler/src/compiler.rs b/crates/oxide_compiler/src/compiler.rs index bdafe39..e6fbcf3 100644 --- a/crates/oxide_compiler/src/compiler.rs +++ b/crates/oxide_compiler/src/compiler.rs @@ -830,12 +830,14 @@ impl Compiler { } pub fn compile(&self, program: &oxide_parser::Program) -> Result { + crate::compiler_debug!("compile: starting..."); let mut ctx = CompileCtx::new(); ctx.pre_register_builtins(); for stmt in &program.body { self.count_statement(stmt, &mut ctx); } + crate::compiler_debug!("counter: {} instructions estimated", ctx.projected_pc); ctx.max_regs = ctx.max_regs.max(1); ctx.reg_overflow = false; ctx.reset_regs(); @@ -859,6 +861,7 @@ impl Compiler { None => last_result = None, } } + crate::compiler_debug!("emitter: {} bytes emitted", ctx.bytecode.len()); if let Some(r) = last_result { ctx.emit(opcode::encode(OpCode::LOAD_VAR, 0, r, 0)); @@ -883,6 +886,8 @@ impl Compiler { return Err("RangeError: jump offset out of range".into()); } + crate::compiler_debug!("compile: done, {} instructions, {} constants", ctx.bytecode.len(), ctx.constants.len()); + Ok(CompiledModule { bytecode: ctx.bytecode, constants: ctx.constants,