diff --git a/crates/oxide_builtins/src/global.rs b/crates/oxide_builtins/src/global.rs index 118292d2..f8a327c6 100644 --- a/crates/oxide_builtins/src/global.rs +++ b/crates/oxide_builtins/src/global.rs @@ -51,11 +51,23 @@ pub fn global_is_finite(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(JsValue::bool(n.is_finite())) } -fn string_arg(vm: &mut H, args: &[u8]) -> String { +/// `escape`/`unescape` 的字符串参数:按规范 `? ToString` 完整转换。 +/// +/// 对象经 ToPrimitive(string hint) 强制转换,Symbol 抛 TypeError,对象方法 +/// 抛出的原生异常原样传播;缺省参数按 "undefined" 处理。 +fn string_arg_full(vm: &mut H, args: &[u8]) -> Result { if args.len() > 1 { - oxide_runtime_api::to_string(vm.reg(args[1])) + match oxide_runtime_api::to_string_full(vm.reg(args[1]), vm) { + Ok(s) => Ok(s), + Err(_) => { + if let Some(exc) = vm.take_uncaught_value() { + return Err(exc); + } + Err(crate::error::create_type_error(vm, "Cannot convert value to a string")) + } + } } else { - "undefined".to_string() + Ok("undefined".to_string()) } } diff --git a/crates/oxide_builtins/src/global/annex_b.rs b/crates/oxide_builtins/src/global/annex_b.rs index 86baefaa..2af7cf96 100644 --- a/crates/oxide_builtins/src/global/annex_b.rs +++ b/crates/oxide_builtins/src/global/annex_b.rs @@ -89,15 +89,25 @@ fn unescape_string(input: &str) -> String { /// Annex B 的全局 `escape(string)`:除 ASCII 字母数字与 `@*_+-./` 外全部编码。 /// <=0xFF 字符用 `%XX`,其它按 UTF-16 code unit 用 `%uXXXX` 转义。 +/// 参数按 `? ToString` 完整转换:Symbol 抛 TypeError,对象方法抛出的原生 +/// 异常原样传播。 pub fn js_escape(vm: &mut H, args: &[u8]) -> NativeResult { - let input = super::string_arg(vm, args); + let input = match super::string_arg_full(vm, args) { + Ok(s) => s, + Err(exc) => return NativeResult::Err(exc), + }; NativeResult::Ok(vm.new_string(&escape_string(&input))) } /// Annex B 的全局 `unescape(string)`:解码 `escape` 生成的 `%XX`/`%uXXXX` 序列, /// 支持 surrogate pair 合并;无法解析的序列原样保留。 +/// 参数按 `? ToString` 完整转换:Symbol 抛 TypeError,对象方法抛出的原生 +/// 异常原样传播。 pub fn js_unescape(vm: &mut H, args: &[u8]) -> NativeResult { - let input = super::string_arg(vm, args); + let input = match super::string_arg_full(vm, args) { + Ok(s) => s, + Err(exc) => return NativeResult::Err(exc), + }; NativeResult::Ok(vm.new_string(&unescape_string(&input))) }