fix: 修复同步调用参数限制与内建语义错误

This commit is contained in:
2026-06-23 14:38:58 +08:00
parent 2d166cbfa5
commit 69500513b3
13 changed files with 675 additions and 189 deletions
+83 -61
View File
@@ -8,7 +8,7 @@ use crate::vm::Vm;
macro_rules! array_ptr {
($vm:expr, $args:expr) => {{
match get_this_array_ref($vm.reg($args[0])) {
match get_this_array_ref($vm, $vm.reg($args[0])) {
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
}
@@ -23,21 +23,36 @@ macro_rules! array_ptr_len {
}};
}
fn get_this_array_ref(val: JsValue) -> Result<*mut JsObject, JsValue> {
fn array_type_error(vm: &mut Vm, msg: &str) -> JsValue {
crate::builtins::error::create_type_error(vm, msg)
}
fn get_this_array_ref(vm: &mut Vm, val: JsValue) -> Result<*mut JsObject, JsValue> {
if !val.is_object() {
return Err(JsValue::undefined());
return Err(array_type_error(vm, "Array method called on incompatible receiver"));
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return Err(JsValue::undefined());
return Err(array_type_error(vm, "Array method called on incompatible receiver"));
}
let obj = unsafe { &*ptr };
if !obj.is_array() {
return Err(JsValue::undefined());
return Err(array_type_error(vm, "Array method called on incompatible receiver"));
}
Ok(ptr)
}
fn require_callback(vm: &mut Vm, callback_val: JsValue) -> Result<JsValue, JsValue> {
if !callback_val.is_object() {
return Err(array_type_error(vm, "callback is not a function"));
}
let ptr = callback_val.as_js_object_ptr();
if ptr.is_null() || !unsafe { &*ptr }.is_function() {
return Err(array_type_error(vm, "callback is not a function"));
}
Ok(callback_val)
}
fn create_new_array(vm: &mut Vm, n: usize) -> *mut JsObject {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
vm.alloc_object(JsObject::new_array(
@@ -431,12 +446,12 @@ pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Err(JsValue::undefined());
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),
};
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) };
@@ -452,12 +467,12 @@ pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Err(JsValue::undefined());
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),
};
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let new_arr = create_new_array(vm, n);
for i in 0..n {
@@ -479,12 +494,12 @@ pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Err(JsValue::undefined());
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),
};
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let mut kept: Vec<JsValue> = Vec::new();
for i in 0..n {
@@ -513,15 +528,15 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_reduce(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if n == 0 && args.len() < 3 {
return NativeResult::Err(JsValue::undefined());
return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value"));
}
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Err(JsValue::undefined());
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),
};
let has_initial = args.len() > 2;
let mut accumulator;
let start_idx;
@@ -552,12 +567,12 @@ pub fn array_reduce(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Ok(JsValue::undefined());
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),
};
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) };
@@ -578,12 +593,12 @@ pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Ok(JsValue::bool(false));
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),
};
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) };
@@ -604,12 +619,12 @@ pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Ok(JsValue::bool(false));
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),
};
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) };
@@ -630,12 +645,12 @@ pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
}
let callback_val = vm.reg(args[1]);
if !callback_val.is_object() {
return NativeResult::Err(JsValue::undefined());
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),
};
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
let mut flat: Vec<JsValue> = Vec::new();
for i in 0..n {
@@ -794,9 +809,12 @@ pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Ok(JsValue::int(-1));
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = vm.reg(args[1]);
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => 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) };
@@ -819,9 +837,12 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
(arr_ptr, len as i32)
};
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = vm.reg(args[1]);
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
for i in (0..n).rev() {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
@@ -841,14 +862,17 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
pub fn array_reduce_right(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = vm.reg(args[1]);
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => 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 {
return NativeResult::Err(JsValue::undefined());
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)
};
@@ -874,15 +898,13 @@ pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult {
let mut vals: Vec<JsValue> = (0..len).map(|i| unsafe { (*arr_ptr).get_prop_at(i) }).collect();
let comparator = if args.len() > 1 {
let candidate = vm.reg(args[1]);
if candidate.is_object() {
let ptr = candidate.as_js_object_ptr();
if !ptr.is_null() && unsafe { &*ptr }.is_function() {
Some(candidate)
} else {
None
}
} else {
if candidate.is_undefined() {
None
} else {
match require_callback(vm, candidate) {
Ok(callback) => Some(callback),
Err(err) => return NativeResult::Err(err),
}
}
} else {
None
+19 -4
View File
@@ -69,14 +69,29 @@ pub(crate) fn new_array_buffer(vm: &mut Vm, data: Vec<u8>) -> *mut JsObject {
vm.alloc_object(obj)
}
pub(crate) fn drop_array_buffer_native(obj: &mut JsObject) -> u64 {
fn array_buffer_vec_ptr(obj: &JsObject) -> Option<*mut Vec<u8>> {
if !obj.is_array_buffer_obj() {
return 0;
return None;
}
let Some(ptr) = obj.native_fn() else {
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut Vec<u8>)
}
pub(crate) fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) {
let Some(ptr) = array_buffer_vec_ptr(old_obj) else {
return;
};
if ptr.is_null() {
return;
}
let cloned = unsafe { (&*ptr).clone() };
let cloned_ptr = Box::into_raw(Box::new(cloned));
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 {
let Some(data_ptr) = array_buffer_vec_ptr(obj) else {
return 0;
};
let data_ptr = ptr.as_ptr() as *mut Vec<u8>;
if data_ptr.is_null() {
return 0;
}
+44 -1
View File
@@ -10,6 +10,37 @@ pub fn number_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
} 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);
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let proto_ptr = unsafe { (*ptr).proto().as_js_object_ptr() };
!proto_ptr.is_null() && std::ptr::eq(proto_ptr, number_proto)
}
} else {
false
}
} else {
false
};
if is_ctor {
let this_val = vm.reg(args[0]);
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
obj.type_tag = oxide_types::object::JsObject::OBJ_TYPE_NUMBER_OBJ;
let boxed = if n.fract() == 0.0 && n.is_finite() && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
JsValue::int(n as i32)
} else {
JsValue::float(n)
};
obj.set_prop_at(0, boxed);
return NativeResult::Ok(this_val);
}
if n.fract() == 0.0 && n.is_finite() && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
NativeResult::Ok(JsValue::int(n as i32))
} else {
@@ -222,5 +253,17 @@ pub fn number_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
if this_val.is_int() || this_val.is_double() {
return NativeResult::Ok(this_val);
}
NativeResult::Ok(JsValue::float(f64::NAN))
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
if obj.is_number_obj() {
return NativeResult::Ok(obj.get_prop_at(0));
}
}
}
NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Number.prototype.valueOf called on incompatible receiver",
))
}