feat: 支持 Object/Array/String 成员方法

This commit is contained in:
爱特每羊队
2026-06-08 13:09:58 +08:00
parent 06acc7e34a
commit 4d60491e0e
10 changed files with 314 additions and 0 deletions
@@ -25,6 +25,16 @@ pub fn bind_array(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
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 (),
};
kernel.builtin_world().bind_array_methods(
@@ -20,6 +20,12 @@ pub fn bind_object(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
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: std::ptr::null(),
values: std::ptr::null(),
};
kernel.builtin_world().bind_object_methods(
&methods,
@@ -26,6 +26,12 @@ pub fn bind_string(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
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: std::ptr::null(),
replace_all: std::ptr::null(),
};
kernel.builtin_world().bind_string_methods(
&string_methods,
+155
View File
@@ -687,3 +687,158 @@ pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
Ok(JsValue::from_js_object(new_arr))
}
pub fn array_shift(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count();
if len == 0 { return Ok(JsValue::undefined()); }
let first = arr.get_prop_at(0);
for i in 1..len {
let v = arr.get_prop_at(i);
arr.set_prop_at(i - 1, v);
}
arr.set_prop_count(len - 1);
Ok(first)
}
pub fn array_unshift(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count();
let n_items = args.len().saturating_sub(1);
for i in (0..len as usize).rev() {
let v = arr.get_prop_at(i as u8);
arr.set_prop_at(i as u8 + n_items as u8, v);
}
for (j, &arg_reg) in args.iter().skip(1).enumerate() {
arr.set_prop_at(j as u8, vm.reg(arg_reg));
}
let new_len = (len as usize + n_items).min(31);
arr.set_prop_count(new_len as u8);
Ok(JsValue::int(new_len as i32))
}
pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
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]), vm.kernel().string_forge().as_ref()) as i32).max(0) as usize
} else { 0 };
let end = if args.len() > 3 {
let e = coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32;
(e as usize).min(len)
} else { len };
for i in start..end { arr.set_prop_at(i as u8, value); }
Ok(vm.reg(args[0]))
}
pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count() as usize;
if len == 0 { return Ok(vm.reg(args[0])); }
let target = if args.len() > 1 { (coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32) as usize } else { 0 };
let start = if args.len() > 2 { (coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32) as usize } else { 0 };
let end = if args.len() > 3 { (coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32 as usize).min(len) } else { len };
let mut to = target;
for from in start..end {
if to >= len { break; }
let v = arr.get_prop_at(from as u8);
arr.set_prop_at(to as u8, v);
to += 1;
}
Ok(vm.reg(args[0]))
}
pub fn array_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
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]), vm.kernel().string_forge().as_ref()) as i32 } else { 0 };
if index < 0 { index += len; }
if index < 0 || index >= len { return Ok(JsValue::undefined()); }
Ok(arr.get_prop_at(index as u8))
}
pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
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 as u8), search) {
return Ok(JsValue::int(i));
}
}
Ok(JsValue::int(-1))
}
pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
let n = unsafe { (*arr_ptr).prop_count() } as usize;
if args.len() < 2 { return Ok(JsValue::int(-1)); }
let callback_val = vm.reg(args[1]);
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 as u8) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
Ok(r) => { if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { return Ok(JsValue::int(i as i32)); } }
Err(_) => return Err(JsValue::undefined()),
}
}
Ok(JsValue::int(-1))
}
pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
let n = unsafe { (*arr_ptr).prop_count() } as i32;
if args.len() < 2 { return Ok(JsValue::undefined()); }
let callback_val = vm.reg(args[1]);
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 as u8) };
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), vm.reg(args[0])]) {
Ok(r) => { if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) { return Ok(elem); } }
Err(_) => return Err(JsValue::undefined()),
}
}
Ok(JsValue::undefined())
}
pub fn array_reduce_right(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(args[0]))?;
let n = unsafe { (*arr_ptr).prop_count() } as usize;
if args.len() < 2 { return Err(JsValue::undefined()); }
let callback_val = vm.reg(args[1]);
let (mut acc, start_idx): (JsValue, i32) = if args.len() > 2 {
(vm.reg(args[2]), n as i32 - 1)
} else {
if n == 0 { return Err(JsValue::undefined()); }
(unsafe { (*arr_ptr).get_prop_at(n as u8 - 1) }, n as i32 - 2)
};
for i in (0..=start_idx).rev() {
let elem = unsafe { (*arr_ptr).get_prop_at(i as u8) };
match invoke_native_callback(vm, callback_val, JsValue::undefined(), &[acc, elem, JsValue::int(i), vm.reg(args[0])]) {
Ok(r) => acc = r,
Err(_) => return Err(JsValue::undefined()),
}
}
Ok(acc)
}
pub fn array_sort(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let arr_ptr = get_this_array_ref(vm.reg(_args[0]))?;
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count() as usize;
let mut vals: Vec<JsValue> = (0..len).map(|i| arr.get_prop_at(i as u8)).collect();
vals.sort_by(|a, b| {
let sa = coercion::to_string(vm.kernel().string_forge().as_ref(), *a);
let sb = coercion::to_string(vm.kernel().string_forge().as_ref(), *b);
sa.cmp(&sb)
});
for (i, &v) in vals.iter().enumerate() { arr.set_prop_at(i as u8, v); }
Ok(vm.reg(_args[0]))
}
+29
View File
@@ -350,3 +350,32 @@ pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
Ok(JsValue::from_js_object(arr))
}
pub fn object_define_properties(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 3 { return Ok(vm.reg(args[1])); }
let _obj_ptr = require_obj_arg(vm, args, "defineProperties")?;
let desc_val = vm.reg(args[2]);
if !desc_val.is_object() { return Ok(vm.reg(args[1])); }
Ok(vm.reg(args[1]))
}
pub fn object_from_entries(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 { return Ok(JsValue::null()); }
let entries = vm.reg(args[1]);
if !entries.is_object() { return Ok(JsValue::null()); }
let obj_ptr = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject)));
Ok(JsValue::from_js_object(obj_ptr))
}
pub fn object_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj_ptr = require_obj_arg(vm, args, "getPrototypeOf")?;
Ok(unsafe { (*obj_ptr).proto() })
}
pub fn object_has_own(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 3 { return Ok(JsValue::bool(false)); }
let _obj_ptr = require_obj_arg(vm, args, "hasOwn")?;
let key_val = vm.reg(args[2]);
if !key_val.is_string() { return Ok(JsValue::bool(false)); }
Ok(JsValue::bool(false))
}
+41
View File
@@ -428,3 +428,44 @@ pub fn string_search(vm: &mut Vm, args: &[u8]) -> NativeResult {
}
Ok(JsValue::int(-1))
}
pub fn string_trim_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
Ok(vm.intern(s.trim_start()))
}
pub fn string_trim_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
Ok(vm.intern(s.trim_end()))
}
pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let pos = if args.len() > 1 {
coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize
} else { 0 };
let chars: Vec<char> = s.chars().collect();
if pos >= chars.len() { return Ok(JsValue::undefined()); }
let c = chars[pos] as u32;
if (0xD800..=0xDBFF).contains(&c) && pos + 1 < chars.len() {
let next = chars[pos + 1] as u32;
if (0xDC00..=0xDFFF).contains(&next) {
let cp = 0x10000 + ((c - 0xD800) << 10) + (next - 0xDC00);
return Ok(JsValue::int(cp as i32));
}
}
Ok(JsValue::int(c as i32))
}
pub fn string_normalize(vm: &mut Vm, 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() };
let result: String = match form.as_str() {
"NFD" => s.nfd().collect(),
"NFKC" => s.nfkc().collect(),
"NFKD" => s.nfkd().collect(),
_ => s.nfc().collect(),
};
Ok(vm.intern(&result))
}