fix: as_string 改走 to_string_full,split 补正则回退与 undefined 分隔符

This commit is contained in:
2026-06-29 00:37:51 +08:00
parent 22aae05749
commit 046119c22c
+21 -24
View File
@@ -9,7 +9,12 @@ use crate::builtins_debug;
use crate::builtins_error; use crate::builtins_error;
fn this_string<H: VmHost>(vm: &H, args: &[u8]) -> String { fn this_string<H: VmHost>(vm: &H, args: &[u8]) -> String {
oxide_runtime_api::to_string(vm.reg(args[0])) let this_val = vm.reg(args[0]);
if this_val.is_null() || this_val.is_undefined() {
String::new() // caller should check and throw TypeError
} else {
oxide_runtime_api::to_string(this_val)
}
} }
pub fn string_from_char_code<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn string_from_char_code<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
@@ -116,8 +121,8 @@ fn make_string_array<H: VmHost>(vm: &mut H, parts: &[String]) -> JsValue {
JsValue::from_js_object(arr) JsValue::from_js_object(arr)
} }
fn as_string<H: VmHost>(_vm: &H, val: JsValue) -> String { fn as_string<H: VmHost>(vm: &mut H, val: JsValue) -> String {
oxide_runtime_api::to_string(val) oxide_runtime_api::to_string_full(val, vm).unwrap_or_else(|_| oxide_runtime_api::to_string(val))
} }
fn char_len(s: &str) -> usize { fn char_len(s: &str) -> usize {
@@ -515,45 +520,35 @@ pub fn string_ends_with<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
pub fn string_split<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult { pub fn string_split<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
builtins_debug!("String.prototype.split called with {} args", args.len()); builtins_debug!("String.prototype.split called with {} args", args.len());
let s = this_string(vm, args); let s = this_string(vm, args);
if args.len() < 2 { // Spec: if separator is undefined, return [string]
if args.len() < 2 || vm.reg(args[1]).is_undefined() {
let parts = vec![s.clone()]; let parts = vec![s.clone()];
return NativeResult::Ok(make_string_array(vm, &parts)); return NativeResult::Ok(make_string_array(vm, &parts));
} }
let sep_val = vm.reg(args[1]); let sep_val = vm.reg(args[1]);
// ToUint32(limit), default 2^32-1
let limit = if args.len() > 2 { let limit = if args.len() > 2 {
oxide_runtime_api::to_integer_or_infinity(vm.reg(args[2])) as usize let l = oxide_runtime_api::to_integer_or_infinity(vm.reg(args[2]));
if l.is_infinite() { u32::MAX as usize } else { (l.max(0.0).trunc() as u64).min(u32::MAX as u64) as usize }
} else { } else {
usize::MAX u32::MAX as usize
}; };
if is_regexp_obj(sep_val, vm) { if is_regexp_obj(sep_val, vm) {
let re_ptr = sep_val.as_js_object_ptr(); let re_ptr = sep_val.as_js_object_ptr();
let re = unsafe { &*re_ptr }; let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() { if let Some(fn_ptr) = re.native_fn() {
Some(p) => p,
None => {
let parts = vec![s.clone()];
return NativeResult::Ok(make_string_array(vm, &parts));
}
};
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) }; let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let mut parts: Vec<String> = Vec::new(); let mut parts: Vec<String> = Vec::new();
let mut last_end = 0; let mut last_end = 0;
for caps in regex.captures_iter(&s) { for caps in regex.captures_iter(&s) {
if parts.len() >= limit { if parts.len() >= limit { break; }
break;
}
let full = caps.get(0).unwrap(); let full = caps.get(0).unwrap();
parts.push(s[last_end..full.start()].to_string()); parts.push(s[last_end..full.start()].to_string());
if parts.len() >= limit { if parts.len() >= limit { break; }
break;
}
for i in 1..caps.len() { for i in 1..caps.len() {
if parts.len() >= limit { if parts.len() >= limit { break; }
break; parts.push(caps.get(i).map(|m| m.as_str()).unwrap_or("").to_string());
}
let cap = caps.get(i).map(|m| m.as_str()).unwrap_or("");
parts.push(cap.to_string());
} }
last_end = full.end(); last_end = full.end();
} }
@@ -562,6 +557,8 @@ pub fn string_split<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
} }
return NativeResult::Ok(make_string_array(vm, &parts)); return NativeResult::Ok(make_string_array(vm, &parts));
} }
// Fall back to string path for regex-like objects without native engine regex
}
let sep = as_string(vm, sep_val); let sep = as_string(vm, sep_val);
let parts: Vec<String> = if sep.is_empty() { let parts: Vec<String> = if sep.is_empty() {