feat: 绑定全局 escape/unescape 与缺失全局桩函数
This commit is contained in:
@@ -1,7 +1,131 @@
|
||||
use std::sync::Arc;
|
||||
use crate::coercion;
|
||||
use crate::native::NativeResult;
|
||||
use crate::vm::Vm;
|
||||
|
||||
use oxide_kernel::kernel::KernelCore;
|
||||
const ESCAPE_SAFE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./";
|
||||
|
||||
pub fn register_global_properties(_kernel: &Arc<KernelCore>, _bump: &bumpalo::Bump) -> Result<(), String> {
|
||||
Ok(())
|
||||
fn escape_string(input: &str) -> String {
|
||||
let mut out = String::with_capacity(input.len());
|
||||
for ch in input.chars() {
|
||||
if ESCAPE_SAFE.contains(ch) {
|
||||
out.push(ch);
|
||||
continue;
|
||||
}
|
||||
|
||||
let code = ch as u32;
|
||||
if code <= 0xFF {
|
||||
out.push('%');
|
||||
out.push_str(&format!("{code:02X}"));
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut buf = [0u16; 2];
|
||||
for unit in ch.encode_utf16(&mut buf).iter() {
|
||||
out.push_str(&format!("%u{:04X}", *unit));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn parse_hex_u8(slice: &[u8]) -> Option<u8> {
|
||||
std::str::from_utf8(slice).ok().and_then(|s| u8::from_str_radix(s, 16).ok())
|
||||
}
|
||||
|
||||
fn parse_hex_u16(slice: &[u8]) -> Option<u16> {
|
||||
std::str::from_utf8(slice).ok().and_then(|s| u16::from_str_radix(s, 16).ok())
|
||||
}
|
||||
|
||||
fn unescape_string(input: &str) -> String {
|
||||
let bytes = input.as_bytes();
|
||||
let mut out = String::with_capacity(input.len());
|
||||
let mut i = 0;
|
||||
|
||||
while i < bytes.len() {
|
||||
if bytes[i] == b'%' {
|
||||
if i + 6 <= bytes.len() && bytes[i + 1] == b'u' {
|
||||
if let Some(unit) = parse_hex_u16(&bytes[i + 2..i + 6]) {
|
||||
if (0xD800..=0xDBFF).contains(&unit)
|
||||
&& i + 12 <= bytes.len()
|
||||
&& bytes[i + 6] == b'%'
|
||||
&& bytes[i + 7] == b'u'
|
||||
{
|
||||
if let Some(low) = parse_hex_u16(&bytes[i + 8..i + 12]) {
|
||||
if (0xDC00..=0xDFFF).contains(&low) {
|
||||
let pair = [unit, low];
|
||||
let decoded = char::decode_utf16(pair).next();
|
||||
match decoded {
|
||||
Some(Ok(ch)) => out.push(ch),
|
||||
_ => {
|
||||
out.push_str(&input[i..i + 12]);
|
||||
}
|
||||
}
|
||||
i += 12;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ch) = char::from_u32(unit as u32) {
|
||||
out.push(ch);
|
||||
} else {
|
||||
out.push_str(&input[i..i + 6]);
|
||||
}
|
||||
i += 6;
|
||||
continue;
|
||||
}
|
||||
} else if i + 3 <= bytes.len() {
|
||||
if let Some(value) = parse_hex_u8(&bytes[i + 1..i + 3]) {
|
||||
out.push(value as char);
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ch = input[i..].chars().next().expect("valid utf-8 char boundary");
|
||||
out.push(ch);
|
||||
i += ch.len_utf8();
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
pub fn js_escape(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let input = if args.len() > 1 {
|
||||
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
|
||||
} else {
|
||||
"undefined".to_string()
|
||||
};
|
||||
NativeResult::Ok(vm.intern(&escape_string(&input)))
|
||||
}
|
||||
|
||||
pub fn js_unescape(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let input = if args.len() > 1 {
|
||||
coercion::to_string(vm.kernel_core().string_forge().as_ref(), vm.reg(args[1]))
|
||||
} else {
|
||||
"undefined".to_string()
|
||||
};
|
||||
NativeResult::Ok(vm.intern(&unescape_string(&input)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{escape_string, unescape_string};
|
||||
|
||||
#[test]
|
||||
fn escape_encodes_spaces_and_unicode() {
|
||||
assert_eq!(escape_string("hello world"), "hello%20world");
|
||||
assert_eq!(escape_string("AΩ你"), "A%u03A9%u4F60");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unescape_decodes_percent_sequences() {
|
||||
assert_eq!(unescape_string("hello%20world"), "hello world");
|
||||
assert_eq!(unescape_string("%u03A9%u4F60"), "Ω你");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unescape_keeps_invalid_sequences() {
|
||||
assert_eq!(unescape_string("%uXYZ1%2G"), "%uXYZ1%2G");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user