diff --git a/crates/oxide_parser/tests/parse_smoke.rs b/crates/oxide_parser/tests/parse_smoke.rs index fd5c6daa..3e220993 100644 --- a/crates/oxide_parser/tests/parse_smoke.rs +++ b/crates/oxide_parser/tests/parse_smoke.rs @@ -50,6 +50,45 @@ fn parse_multiple_statements() { assert_eq!(program.body.len(), 3, "should have 3 statements"); } +#[test] +fn parse_unicode_escape_insufficient_hex_digits() { + // \u 后不足 4 位 hex 即非法转义:解析期报 SyntaxError。 + let allocator = Allocator::default(); + let result = oxide_parser::parse(&allocator, "\"\\u12\".length"); + assert!(result.is_err(), "\"\\u12\"(不足 4 位 hex)应解析失败"); + assert_eq!( + result.unwrap_err().first().map(|e| e.message.as_str()), + Some("Invalid escape sequence"), + "诊断口径应与现行非法转义消息一致" + ); +} + +#[test] +fn parse_unicode_escape_non_hex_digits() { + // \u 后 4 位含非 hex 字符即非法转义:解析期报 SyntaxError。 + let allocator = Allocator::default(); + let result = oxide_parser::parse(&allocator, "\"\\uZZZZ\".length"); + assert!(result.is_err(), "\"\\uZZZZ\"(非 hex 位)应解析失败"); + assert_eq!( + result.unwrap_err().first().map(|e| e.message.as_str()), + Some("Invalid escape sequence"), + "诊断口径应与现行非法转义消息一致" + ); +} + +#[test] +fn parse_unicode_brace_escape_out_of_code_point_range() { + // 花括号 \u{...} 值超 U+10FFFF 即非法码点:解析期报 SyntaxError。 + let allocator = Allocator::default(); + let result = oxide_parser::parse(&allocator, "\"\\u{110000}\".length"); + assert!(result.is_err(), "\"\\u{{110000}}\"(超 0x10FFFF)应解析失败"); + assert_eq!( + result.unwrap_err().first().map(|e| e.message.as_str()), + Some("Invalid escape sequence"), + "诊断口径应与现行非法转义消息一致" + ); +} + #[test] fn parse_concise_arrow_array_member_call() { // concise 箭头体 + 数组字面量 + 成员调用形是合法 JS:必须解析成功。 diff --git a/crates/oxide_vm/tests/builtins_string_tests.rs b/crates/oxide_vm/tests/builtins_string_tests.rs index 5d539700..35b2128f 100644 --- a/crates/oxide_vm/tests/builtins_string_tests.rs +++ b/crates/oxide_vm/tests/builtins_string_tests.rs @@ -1374,3 +1374,33 @@ fn utf16_fffd_hex4_key_materialization() { .unwrap(); assert!(r.as_bool()); } + +#[test] +fn unicode_escape_exactly_four_hex_digits() { + // \u 恰取 4 位 hex:第 5 位 hex 数字是串内字面字符。 + // "\u10400" = U+1040 + '0',长度 2,码位 [0x1040, 0x30]。 + let mut vm = Vm::new(); + let r = eval( + &mut vm, + "\"\\u10400\".length === 2 && \"\\u10400\".charCodeAt(0) === 0x1040 && \"\\u10400\".charCodeAt(1) === 0x30", + ) + .unwrap(); + assert!(r.as_bool()); +} + +#[test] +fn unicode_brace_escape_supplementary_and_lone_surrogate() { + // 花括号 \u{...} 取任意长度 hex:超平面码点 U+10400 落 UTF-16 单元对 + // (长度 2、codePointAt(0) = 0x10400、与 \uD801\uDC00 形态等值); + // \u{D800} 现行 spec 无 surrogate 限制——孤立 surrogate 字面量合法(与 + // V8/node 同接受),长度 1 且与 \ud800 形态等值。 + let mut vm = Vm::new(); + let r = eval( + &mut vm, + "\"\\u{10400}\".length === 2 && \"\\u{10400}\".codePointAt(0) === 0x10400 \ + && \"\\u{10400}\" === \"\\uD801\\uDC00\" \ + && \"\\u{D800}\".length === 1 && \"\\u{D800}\" === \"\\ud800\"", + ) + .unwrap(); + assert!(r.as_bool()); +}