diff --git a/crates/oxide_builtins/src/date.rs b/crates/oxide_builtins/src/date.rs index 8c7cc48f..e9a3c694 100644 --- a/crates/oxide_builtins/src/date.rs +++ b/crates/oxide_builtins/src/date.rs @@ -211,7 +211,7 @@ pub fn date_constructor(vm: &mut H, args: &[u8]) -> NativeResult { } else { "Invalid Date".to_string() }; - return NativeResult::Ok(vm.new_string(&s)); + return NativeResult::Ok(vm.new_string_owned(s)); } let mut obj = JsObject::new_empty( @@ -866,7 +866,7 @@ fn date_to_string_inner(vm: &mut H, args: &[u8], format_str: &str, in Some(dt) => dt.format(format_str).to_string(), None => invalid.to_string(), }; - NativeResult::Ok(vm.new_string(&s)) + NativeResult::Ok(vm.new_string_owned(s)) } /// `Date.prototype.toISOString()`:输出 ISO 8601 格式(`YYYY-MM-DDTHH:MM:SS.mmmZ`)。 @@ -885,7 +885,7 @@ pub fn date_to_json(vm: &mut H, args: &[u8]) -> NativeResult { Some(dt) => dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(), None => return NativeResult::Ok(JsValue::null()), }; - NativeResult::Ok(vm.new_string(&s)) + NativeResult::Ok(vm.new_string_owned(s)) } /// `Date.prototype.toString()`:本地时间完整字符串(如 `Wed Aug 05 2026 12:00:00 GMT+0000`)。 diff --git a/crates/oxide_builtins/src/function.rs b/crates/oxide_builtins/src/function.rs index fc5af599..3e43c2a7 100644 --- a/crates/oxide_builtins/src/function.rs +++ b/crates/oxide_builtins/src/function.rs @@ -274,5 +274,5 @@ pub fn function_to_string(vm: &mut H, args: &[u8]) -> NativeResult { } else { format!("function {name}() {{ {body} }}") }; - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } diff --git a/crates/oxide_builtins/src/json.rs b/crates/oxide_builtins/src/json.rs index e0cc0976..f9e96e61 100644 --- a/crates/oxide_builtins/src/json.rs +++ b/crates/oxide_builtins/src/json.rs @@ -281,7 +281,7 @@ pub fn json_stringify(vm: &mut H, args: &[u8]) -> NativeResult { { return NativeResult::Err(crate::error::create_type_error(vm, "Converting circular structure to JSON")); }; - NativeResult::Ok(vm.new_string(&output)) + NativeResult::Ok(vm.new_string_owned(output)) } #[allow(clippy::too_many_arguments)] diff --git a/crates/oxide_builtins/src/number.rs b/crates/oxide_builtins/src/number.rs index 8f37457a..2241fd02 100644 --- a/crates/oxide_builtins/src/number.rs +++ b/crates/oxide_builtins/src/number.rs @@ -213,7 +213,7 @@ pub fn number_to_string(vm: &mut H, args: &[u8]) -> NativeResult { if neg { result.insert(0, '-'); } - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } /// `Number.prototype.toFixed(digits)`:固定小数位数(0..100)输出字符串, @@ -330,7 +330,7 @@ pub fn number_to_exponential(vm: &mut H, args: &[u8]) -> NativeResult } } let result = format!("{}{}", sign_prefix, s); - return NativeResult::Ok(vm.new_string(&result)); + return NativeResult::Ok(vm.new_string_owned(result)); } let raw = oxide_runtime_api::to_integer_or_infinity(vm.reg(args[1])); if !(0.0..=100.0).contains(&raw) { diff --git a/crates/oxide_builtins/src/regexp.rs b/crates/oxide_builtins/src/regexp.rs index e2666114..8f5f30b4 100644 --- a/crates/oxide_builtins/src/regexp.rs +++ b/crates/oxide_builtins/src/regexp.rs @@ -275,7 +275,7 @@ pub fn regexp_to_string(vm: &mut H, args: &[u8]) -> NativeResult { vm.lookup_str(val).unwrap_or_default() }; let result = format!("/{}/{}", source, flags); - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } // RegExp 对象 hash 槽中属性的固定下标(构造顺序:lastIndex/source/flags/global/...)。 diff --git a/crates/oxide_builtins/src/string.rs b/crates/oxide_builtins/src/string.rs index 2260d30e..873ba5b6 100644 --- a/crates/oxide_builtins/src/string.rs +++ b/crates/oxide_builtins/src/string.rs @@ -51,7 +51,7 @@ pub fn string_from_char_code(vm: &mut H, args: &[u8]) -> NativeResult out.push('\u{FFFD}'); } } - NativeResult::Ok(vm.new_string(&out)) + NativeResult::Ok(vm.new_string_owned(out)) } /// `String.fromCodePoint(...codes)`:把各参数按 ToNumber 语义转成 code point @@ -84,7 +84,7 @@ pub fn string_from_code_point(vm: &mut H, args: &[u8]) -> NativeResul out.push(char::from_u32(code).unwrap()); } } - NativeResult::Ok(vm.new_string(&out)) + NativeResult::Ok(vm.new_string_owned(out)) } /// `String.prototype.valueOf`:返回包装对象的原始字符串;其它 this 抛 TypeError。 @@ -127,7 +127,7 @@ pub fn string_constructor(vm: &mut H, args: &[u8]) -> NativeResult { } else { String::new() }; - let str_val = vm.new_string(&s); + let str_val = vm.new_string_owned(s); let string_proto = vm.session().builtin_world().string_proto.as_ptr() as *mut JsObject; let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] }); @@ -309,7 +309,7 @@ pub(crate) fn regex_replace_fn( last_end = range.end; } out.push_str(&text[last_end..]); - NativeResult::Ok(vm.new_string(&out)) + NativeResult::Ok(vm.new_string_owned(out)) } /// 字符串模式 + 函数 replacer:all 全替换否则替换首个。回调参数 @@ -351,7 +351,7 @@ fn string_replace_fn(vm: &mut H, text: &str, pattern: &str, replacer: last_end = p + search_length; } out.push_str(&text[last_end..]); - NativeResult::Ok(vm.new_string(&out)) + NativeResult::Ok(vm.new_string_owned(out)) } fn char_len(s: &str) -> usize { @@ -507,7 +507,7 @@ pub fn string_concat(vm: &mut H, args: &[u8]) -> NativeResult { } } } - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } /// `String.prototype.slice(start, end)`:按字符区间(支持负索引)取子串。 @@ -594,7 +594,7 @@ pub fn string_substr(vm: &mut H, args: &[u8]) -> NativeResult { len as usize - start }; let result = take_chars(&s[byte_index_at_char(&s, start)..], length.min(len as usize - start)); - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } /// `String.prototype.at(index)`:按字符索引取字符(支持负索引);越界返回 undefined。 @@ -658,14 +658,14 @@ pub fn string_last_index_of(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 = try_string!(this_string(vm, args)); - NativeResult::Ok(vm.new_string(&s.to_uppercase())) + NativeResult::Ok(vm.new_string_owned(s.to_uppercase())) } /// `String.prototype.toLowerCase`:全小写转换。 pub fn string_to_lower_case(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.toLowerCase called with {} args", args.len()); let s = try_string!(this_string(vm, args)); - NativeResult::Ok(vm.new_string(&s.to_lowercase())) + NativeResult::Ok(vm.new_string_owned(s.to_lowercase())) } /// `String.prototype.trim`:去除两端空白。 @@ -684,7 +684,7 @@ pub fn string_repeat(vm: &mut H, args: &[u8]) -> NativeResult { } else { 1 }; - NativeResult::Ok(vm.new_string(&s.repeat(n))) + NativeResult::Ok(vm.new_string_owned(s.repeat(n))) } /// `String.prototype.padStart(targetLength, padString)`:在头部补足 padString 到目标长度。 @@ -703,14 +703,14 @@ pub fn string_pad_start(vm: &mut H, args: &[u8]) -> NativeResult { } let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() }; if s_len >= target || pad.is_empty() { - return NativeResult::Ok(vm.new_string(&s)); + return NativeResult::Ok(vm.new_string_owned(s)); } let needed = target - s_len; let pad_len = char_len(&pad).max(1); let reps = needed.div_ceil(pad_len); let mut out = take_chars(&pad.repeat(reps), needed); out.push_str(&s); - NativeResult::Ok(vm.new_string(&out)) + NativeResult::Ok(vm.new_string_owned(out)) } /// `String.prototype.padEnd(targetLength, padString)`:在尾部补足 padString 到目标长度。 @@ -729,14 +729,14 @@ pub fn string_pad_end(vm: &mut H, args: &[u8]) -> NativeResult { } let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() }; if s_len >= target || pad.is_empty() { - return NativeResult::Ok(vm.new_string(&s)); + return NativeResult::Ok(vm.new_string_owned(s)); } let needed = target - s_len; let pad_len = char_len(&pad).max(1); let reps = needed.div_ceil(pad_len); let mut out = s; out.push_str(&take_chars(&pad.repeat(reps), needed)); - NativeResult::Ok(vm.new_string(&out)) + NativeResult::Ok(vm.new_string_owned(out)) } /// `String.prototype.startsWith(searchString, position)`:是否以指定子串开头。 @@ -846,7 +846,7 @@ pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.replace called with {} args", args.len()); let s = try_string!(this_string(vm, args)); if args.len() < 2 { - return NativeResult::Ok(vm.new_string(&s)); + return NativeResult::Ok(vm.new_string_owned(s)); } let pattern_val = vm.reg(args[1]); @@ -870,7 +870,7 @@ pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() }; // 手动展开 $ 引用(regress 的 replace 对 $n 展开为空)。 let result = regex_replace_manual(regex, &s, &replacement, is_global); - return NativeResult::Ok(vm.new_string(&result)); + return NativeResult::Ok(vm.new_string_owned(result)); } // 无原生正则的类正则对象回退到字符串路径。 } @@ -887,7 +887,7 @@ pub fn string_replace(vm: &mut H, args: &[u8]) -> NativeResult { } let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() }; let result = s.replacen(&pattern, &replacement, 1); - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } /// `String.prototype.match(pattern)`:按 RegExp 匹配;global 返回全部匹配数组, @@ -1059,7 +1059,7 @@ pub fn string_to_well_formed(vm: &mut H, args: &[u8]) -> NativeResult out.push(c); } } - NativeResult::Ok(vm.new_string(&out)) + NativeResult::Ok(vm.new_string_owned(out)) } /// `String.prototype.normalize(form)`:按 NFC/NFD/NFKC/NFKD 规范化为 Unicode 规范形式。 @@ -1074,7 +1074,7 @@ pub fn string_normalize(vm: &mut H, args: &[u8]) -> NativeResult { "NFKD" => s.nfkd().collect(), _ => s.nfc().collect(), }; - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } pub(crate) const MALL_INPUT: &str = "__mal_input__"; @@ -1234,7 +1234,7 @@ pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { builtins_debug!("String.prototype.replaceAll called with {} args", args.len()); let s = try_string!(this_string(vm, args)); if args.len() < 2 { - return NativeResult::Ok(vm.new_string(&s)); + return NativeResult::Ok(vm.new_string_owned(s)); } let pattern_val = vm.reg(args[1]); if is_regexp_obj(pattern_val, vm) { @@ -1242,7 +1242,7 @@ pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { let re = unsafe { &*re_ptr }; let fn_ptr = match re.native_fn() { Some(p) => p, - None => return NativeResult::Ok(vm.new_string(&s)), + None => return NativeResult::Ok(vm.new_string_owned(s)), }; // SAFETY: fn_ptr 持有 regexp_constructor 存放的 `Box` 指针。 let regex = unsafe { &*(fn_ptr.as_ptr() as *const regress::Regex) }; @@ -1257,7 +1257,7 @@ pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { } let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() }; let result = regex_replace_manual(regex, &s, &replacement, true); - return NativeResult::Ok(vm.new_string(&result)); + return NativeResult::Ok(vm.new_string_owned(result)); } let pattern = as_string(vm, pattern_val); if args.len() > 2 { @@ -1271,5 +1271,5 @@ pub fn string_replace_all(vm: &mut H, args: &[u8]) -> NativeResult { } let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() }; let result = s.replace(&pattern, &replacement); - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } diff --git a/crates/oxide_builtins/src/symbol.rs b/crates/oxide_builtins/src/symbol.rs index 04139484..410a1e54 100644 --- a/crates/oxide_builtins/src/symbol.rs +++ b/crates/oxide_builtins/src/symbol.rs @@ -85,7 +85,7 @@ pub fn symbol_to_string(vm: &mut H, args: &[u8]) -> NativeResult { let idx = sym.as_symbol_index(); let desc = vm.symbol_description(idx).unwrap_or("").to_string(); let result = format!("Symbol({})", desc); - NativeResult::Ok(vm.new_string(&result)) + NativeResult::Ok(vm.new_string_owned(result)) } /// `Symbol.prototype.valueOf`:Symbol 原样返回;包装对象解盒返回其原始 Symbol。 diff --git a/crates/oxide_builtins/src/temporal.rs b/crates/oxide_builtins/src/temporal.rs index cc4425b9..c2daa3f9 100644 --- a/crates/oxide_builtins/src/temporal.rs +++ b/crates/oxide_builtins/src/temporal.rs @@ -1134,7 +1134,7 @@ fn instant_default_string(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Err(crate::error::create_range_error(vm, "invalid Instant")); }; match format_instant_iso(epoch_ns, None, true, None) { - Some(output) => NativeResult::Ok(vm.new_string(&output)), + Some(output) => NativeResult::Ok(vm.new_string_owned(output)), None => NativeResult::Err(crate::error::create_range_error(vm, "invalid Instant")), } } @@ -1219,7 +1219,7 @@ pub fn instant_to_string(vm: &mut H, args: &[u8]) -> NativeResult { return NativeResult::Err(crate::error::create_range_error(vm, "Instant outside supported range")); } match format_instant_iso(rounded_ns, offset_minutes, include_seconds, output_digits) { - Some(output) => NativeResult::Ok(vm.new_string(&output)), + Some(output) => NativeResult::Ok(vm.new_string_owned(output)), None => NativeResult::Err(crate::error::create_range_error(vm, "invalid Instant")), } } @@ -1890,7 +1890,7 @@ pub fn duration_to_string(vm: &mut H, args: &[u8]) -> NativeResult { output.push_str("0S"); } } - NativeResult::Ok(vm.new_string(&output)) + NativeResult::Ok(vm.new_string_owned(output)) } /// `Temporal.Duration.prototype.valueOf()` 始终拒绝隐式数值转换。 @@ -3514,7 +3514,7 @@ fn format_plain_date_time_iso( fn plain_date_time_iso_string(vm: &mut H, args: &[u8]) -> NativeResult { let (year, month, day, total_ns) = native_try!(plain_date_time_parts(vm, args)); let output = format_plain_date_time_iso(year, month, day, total_ns as i128, true, None, "never"); - NativeResult::Ok(vm.new_string(&output)) + NativeResult::Ok(vm.new_string_owned(output)) } /// `Temporal.PlainDateTime.prototype.toString(options)`:按精度、舍入模式与日历显示输出 ISO 8601。 @@ -3601,7 +3601,7 @@ pub fn plain_date_time_to_string(vm: &mut H, args: &[u8]) -> NativeRe output_digits, &calendar_name, ); - NativeResult::Ok(vm.new_string(&output)) + NativeResult::Ok(vm.new_string_owned(output)) } /// 按 duration-like 分量对 PlainDateTime 做加减:先平衡时间(溢出为天), diff --git a/crates/oxide_runtime_api/src/lib.rs b/crates/oxide_runtime_api/src/lib.rs index 3391649d..be721357 100644 --- a/crates/oxide_runtime_api/src/lib.rs +++ b/crates/oxide_runtime_api/src/lib.rs @@ -75,6 +75,8 @@ pub trait VmHost { // 对象分配 / 字符串创建 fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject; fn new_string(&mut self, s: &str) -> JsValue; + /// move 接收 `String` 创建会话字符串,避免一次整串克隆。 + fn new_string_owned(&mut self, s: String) -> JsValue; /// 分配 BigInt 值(num_bigint::BigInt box 登记到 VM,返回携带指针的 `JsValue`)。 fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue; /// 读取 BigInt 值;调用方须保证 `val.is_bigint()`。 diff --git a/crates/oxide_vm/src/vm.rs b/crates/oxide_vm/src/vm.rs index e04b0460..fa58d048 100644 --- a/crates/oxide_vm/src/vm.rs +++ b/crates/oxide_vm/src/vm.rs @@ -1805,6 +1805,9 @@ impl oxide_runtime_api::VmHost for Vm { fn new_string(&mut self, s: &str) -> JsValue { self.new_string(s) } + fn new_string_owned(&mut self, s: String) -> JsValue { + Vm::new_string_owned(self, s) + } fn new_bigint(&mut self, v: num_bigint::BigInt) -> JsValue { Vm::new_bigint(self, v) }