test: oxide_kernel forge 内联测试移入集成测试

This commit is contained in:
2026-09-16 00:31:44 +00:00
parent 31f5fd8eaa
commit 61f590f118
4 changed files with 321 additions and 325 deletions
-79
View File
@@ -75,82 +75,3 @@ impl Default for PropForge {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_nonexistent() {
let forge = PropForge::new();
assert!(forge.get_template(999).is_none());
}
#[test]
fn test_upsert_and_get() {
let forge = PropForge::new();
let t = PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 10,
};
forge.upsert(1, t);
let got = forge.get_template(1).unwrap();
assert_eq!(got.shape_id, 1);
assert_eq!(got.prop_name, 11);
assert_eq!(got.position, 3);
assert_eq!(got.generation, 10);
}
#[test]
fn test_upsert_if_better_high_gen_wins() {
let forge = PropForge::new();
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 10,
},
);
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 12,
position: 7,
generation: 5,
},
);
let got = forge.get_template(1).unwrap();
assert_eq!(got.generation, 10);
}
#[test]
fn test_upsert_if_better_low_replaced() {
let forge = PropForge::new();
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 5,
},
);
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 12,
position: 7,
generation: 10,
},
);
let got = forge.get_template(1).unwrap();
assert_eq!(got.generation, 10);
assert_eq!(got.position, 7);
}
}
-246
View File
@@ -456,249 +456,3 @@ pub fn typeof_string_ptr(kind: u8) -> *const JsString {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intern_dedup() {
let interner = PermInterner::new();
let (i1, h1) = interner.intern("abc");
let (i2, h2) = interner.intern("abc");
assert_eq!(i1, i2);
assert_eq!(h1, h2);
}
#[test]
fn intern_different() {
let interner = PermInterner::new();
let (i1, _) = interner.intern("x");
let (i2, _) = interner.intern("y");
assert_ne!(i1, i2);
}
#[test]
fn lookup_zero_clone() {
let interner = PermInterner::new();
let (id, _) = interner.intern("hello");
assert_eq!(interner.lookup(id), Some("hello"));
}
#[test]
fn lookup_not_found() {
let interner = PermInterner::new();
assert_eq!(interner.lookup(99999), None);
}
#[test]
fn entry_count_monotonic() {
let interner = PermInterner::new();
assert_eq!(interner.entry_count(), 0);
interner.intern("a");
interner.intern("b");
interner.intern("a");
assert_eq!(interner.entry_count(), 2);
}
#[test]
fn many_unique_no_collision() {
let interner = PermInterner::new();
for i in 0..10_000 {
let s = format!("key{i}");
let (id, _) = interner.intern(&s);
assert_eq!(interner.lookup(id), Some(&*Box::leak(s.into_boxed_str())));
}
assert_eq!(interner.entry_count(), 10_000);
}
#[test]
fn string_ptr_roundtrip() {
let interner = PermInterner::new();
let (id, _) = interner.intern("perm");
let ptr = interner.string_ptr(id);
assert_eq!(unsafe { (*ptr).as_str() }, "perm");
// 二次调用返回同一稳定指针(仅物化一次)。
assert_eq!(interner.string_ptr(id), ptr);
}
#[test]
fn single_char_ptr_idempotent() {
let a = single_char_ptr(b'a');
assert_eq!(unsafe { (*a).as_str() }, "a");
// 二次调用返回同一稳定指针(仅物化一次)。
assert_eq!(single_char_ptr(b'a'), a);
}
#[test]
fn single_char_table_full_ascii() {
// 全表 128 条目均可物化且内容为对应的单字符文本。
for b in 0u8..=127 {
let ptr = single_char_ptr(b);
assert_eq!(unsafe { (*ptr).as_str() }, (b as char).to_string());
}
}
#[test]
fn typeof_table_content_and_stability() {
// 8 个 typeof 结果串与下标约定一一对应,且二次调用返回同一稳定指针。
let texts = ["undefined", "object", "boolean", "number", "string", "symbol", "bigint", "function"];
for (i, t) in texts.iter().enumerate() {
let ptr = typeof_string_ptr(i as u8);
assert_eq!(unsafe { (*ptr).as_str() }, *t);
assert_eq!(typeof_string_ptr(i as u8), ptr, "下标 {i} 二次调用应返回同一指针");
}
}
#[test]
fn encode_key_identity_and_escapes() {
// 良形且无 FFFD(含超平面良配对)编码为恒等文本。
assert_eq!(encode_key(&"abc🚀".encode_utf16().collect::<Vec<u16>>()), "abc🚀");
// 孤立 surrogate:FFFD + 4 位小写十六进制。
assert_eq!(encode_key(&[0xD800]), "\u{FFFD}d800");
assert_eq!(encode_key(&[0xDFFF]), "\u{FFFD}dfff");
assert_eq!(encode_key(&[0xDBFF, 0x61]), "\u{FFFD}dbffa");
// FFFD 单元:FFFD + 字面 "fffd"。
assert_eq!(encode_key(&[0xFFFD]), "\u{FFFD}fffd");
// 两种不同单元序列的键文本互异(解码可区分)。
assert_ne!(encode_key(&[0xFFFD]), encode_key(&[0xD800]));
// 文本 [FFFD,'d','8','0','0'] 与单元 [D800] 的键必须可区分(防物化串键)。
assert_ne!(encode_key(&[0xFFFD, 0x64, 0x38, 0x30, 0x30]), encode_key(&[0xD800]));
}
#[test]
fn encode_decode_roundtrip() {
let cases: Vec<Vec<u16>> = vec![
Vec::new(),
"a".encode_utf16().collect(),
"abc🚀😀".encode_utf16().collect(),
vec![0xD800, 0x42, 0xDC00],
vec![0xFFFD, 0xFFFD, 0x41, 0xFFFD, 0x66, 0x66, 0x66, 0x64],
vec![0xFFFF, 0x0041, 0xDBFF, 0xDC00, 0xD800],
];
for units in &cases {
let key = encode_key(units);
assert_eq!(decode_key(&key), units.as_slice(), "roundtrip 失败: key={key:?}");
}
}
#[test]
fn decode_key_defensive_raw_fffd() {
// 裸 FFFD(文本末尾或后跟非转义文本)还原为 FFFD 单元,后续字符不被吞。
assert_eq!(decode_key("a\u{FFFD}"), &[0x61, 0xFFFD]);
assert_eq!(decode_key("a\u{FFFD}zz"), &[0x61, 0xFFFD, 0x7A, 0x7A]);
}
#[test]
fn source_escape_marker_forms() {
// 孤立 surrogate / FFFD → 源码域 \u 转义形态;配对与 BMP 恒等。
assert_eq!(source_escape(&[0xD800]), "\\ud800");
assert_eq!(source_escape(&[0xDFFF]), "\\udfff");
assert_eq!(source_escape(&[0xFFFD]), "\\ufffd");
assert_eq!(source_escape(&[0x61, 0xD800, 0x62]), "a\\ud800b");
assert_eq!(source_escape(&[0xD83D, 0xDE00]), "\u{1F600}");
}
/// 源码是 JS 程序,反斜杠为语法字符:原样透传,转义文本形态逐字保留
/// (正则/字符串字面量内 `\u{1d306}`、`\\` 等序列不得被改写)。
#[test]
fn source_escape_backslash_passthrough() {
// 正则字面量源文本(u 模式 \u{..} 转义):逐字恒等。
let re_src: Vec<u16> = "/\\u{1d306}/u".encode_utf16().collect();
assert_eq!(source_escape(&re_src), "/\\u{1d306}/u");
// 转义文本(反斜杠 + "ud800" 字面文本)与孤立 surrogate 单元同形:
// 源码域中 `\ud800` 文本即孤立 surrogate 的源码表示,两者编码同一
// 源码文本是设计使然(源码即程序,oxc 只见转义文本)。
let text: Vec<u16> = "\\ud800".encode_utf16().collect();
assert_eq!(source_escape(&text), "\\ud800");
assert_eq!(source_escape(&[0xD800]), source_escape(&text));
// 双反斜杠(字面量内转义反斜杠)恒等。
let dbl: Vec<u16> = "\\\\".encode_utf16().collect();
assert_eq!(source_escape(&dbl), "\\\\");
}
/// 键域闭环:`decode_key ∘ source_escape_to_key ∘ source_escape` 对单元
/// 序列恒等(注入 marker 还原、用户转义文本透传、双反斜杠与 astral 对)。
#[test]
fn source_escape_roundtrip_via_key() {
let cases: Vec<Vec<u16>> = vec![
vec![0xD800],
vec![0xDFFF],
vec![0xFFFD],
vec![0x5C],
// 用户字面转义文本(非 marker 值域 / \u{..} 形态):逐字透传。
vec![0x5C, 0x75, 0x30, 0x30, 0x34, 0x31],
vec![0x5C, 0x75, 0x7B, 0x31, 0x64, 0x33, 0x30, 0x36, 0x7D],
vec![0x5C, 0x5C],
// 数据反斜杠紧邻注入 marker(`\\ud800`/`\\ufffd` 拼接形态)。
vec![0x5C, 0xD800],
vec![0x5C, 0xFFFD],
// FFFD 单元后跟 "d800" 文本:marker 消费恰 4 字符,不吞后续。
vec![0xFFFD, 0x64, 0x38, 0x30, 0x30],
vec![0x66, 0x66, 0x66, 0x64],
vec![0xD83D, 0xDE00],
];
for units in &cases {
let escaped = source_escape(units);
let key = source_escape_to_key(&escaped);
assert_eq!(&decode_key(&key), units.as_slice(), "roundtrip 失败: escaped={escaped:?}");
}
}
#[test]
fn string_ptr_materializes_units() {
let interner = PermInterner::new();
// 良形键:物化内容与旧行为逐位一致,且二次调用返回同一稳定指针。
let (id, _) = interner.intern("perm");
let ptr = interner.string_ptr(id);
assert_eq!(unsafe { (*ptr).as_str() }, "perm");
assert_eq!(interner.string_ptr(id), ptr);
// 含转义的键:物化出孤立 surrogate 单元形态。
let (id2, _) = interner.intern(&encode_key(&[0xD800]));
let ptr2 = interner.string_ptr(id2);
assert!(unsafe { (*ptr2).has_lone_surrogate() });
assert_eq!(unsafe { (*ptr2).units() }, &[0xD800][..]);
}
#[test]
fn perm_table_concurrent_first_use_unique_ptr() {
// 并发首用竞态回归:96 线程同一时刻命中冷表槽,强制多线程同入慢路径。
// 所有调用方必须拿到同一稳定指针,且该指针恒指向存活的 JsString。
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
const N: usize = 96;
let go = Arc::new(AtomicBool::new(false));
let mut handles = Vec::with_capacity(N);
for _ in 0..N {
let go = Arc::clone(&go);
handles.push(std::thread::spawn(move || {
while !go.load(Ordering::Acquire) {
std::hint::spin_loop();
}
// 裸指针 !Send,跨线程以 usize 传递,出口再转回。
let (i, c, t) = (small_int_ptr(42).unwrap(), single_char_ptr(b'x'), typeof_string_ptr(1));
(i as usize, c as usize, t as usize)
}));
}
go.store(true, Ordering::Release);
let mut results = Vec::with_capacity(N);
for h in handles {
results.push(h.join().expect("竞态线程"));
}
let (i0, c0, t0) = results[0];
for (i, c, t) in &results[1..] {
assert_eq!(*i, i0, "小整数表首用应全局唯一指针");
assert_eq!(*c, c0, "单字符表首用应全局唯一指针");
assert_eq!(*t, t0, "typeof 表首用应全局唯一指针");
}
// 返回指针必须指向存活 JsString:内容与长度可回读。
assert_eq!(unsafe { (*(i0 as *const JsString)).as_str() }, "42");
assert_eq!(unsafe { (*(c0 as *const JsString)).as_str() }, "x");
let t0 = t0 as *const JsString;
assert_eq!(unsafe { (*t0).as_str() }, "object");
assert_eq!(unsafe { (*t0).utf16_len() }, 6);
}
}
@@ -0,0 +1,75 @@
use oxide_kernel::prop_forge::{PropForge, PropTemplate};
#[test]
fn test_get_nonexistent() {
let forge = PropForge::new();
assert!(forge.get_template(999).is_none());
}
#[test]
fn test_upsert_and_get() {
let forge = PropForge::new();
let t = PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 10,
};
forge.upsert(1, t);
let got = forge.get_template(1).unwrap();
assert_eq!(got.shape_id, 1);
assert_eq!(got.prop_name, 11);
assert_eq!(got.position, 3);
assert_eq!(got.generation, 10);
}
#[test]
fn test_upsert_if_better_high_gen_wins() {
let forge = PropForge::new();
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 10,
},
);
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 12,
position: 7,
generation: 5,
},
);
let got = forge.get_template(1).unwrap();
assert_eq!(got.generation, 10);
}
#[test]
fn test_upsert_if_better_low_replaced() {
let forge = PropForge::new();
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 11,
position: 3,
generation: 5,
},
);
forge.upsert_if_better(
1,
PropTemplate {
shape_id: 1,
prop_name: 12,
position: 7,
generation: 10,
},
);
let got = forge.get_template(1).unwrap();
assert_eq!(got.generation, 10);
assert_eq!(got.position, 7);
}
@@ -0,0 +1,246 @@
use oxide_kernel::string_forge::{
decode_key, encode_key, single_char_ptr, small_int_ptr, source_escape, source_escape_to_key, typeof_string_ptr,
PermInterner,
};
use oxide_types::object::JsString;
#[test]
fn intern_dedup() {
let interner = PermInterner::new();
let (i1, h1) = interner.intern("abc");
let (i2, h2) = interner.intern("abc");
assert_eq!(i1, i2);
assert_eq!(h1, h2);
}
#[test]
fn intern_different() {
let interner = PermInterner::new();
let (i1, _) = interner.intern("x");
let (i2, _) = interner.intern("y");
assert_ne!(i1, i2);
}
#[test]
fn lookup_zero_clone() {
let interner = PermInterner::new();
let (id, _) = interner.intern("hello");
assert_eq!(interner.lookup(id), Some("hello"));
}
#[test]
fn lookup_not_found() {
let interner = PermInterner::new();
assert_eq!(interner.lookup(99999), None);
}
#[test]
fn entry_count_monotonic() {
let interner = PermInterner::new();
assert_eq!(interner.entry_count(), 0);
interner.intern("a");
interner.intern("b");
interner.intern("a");
assert_eq!(interner.entry_count(), 2);
}
#[test]
fn many_unique_no_collision() {
let interner = PermInterner::new();
for i in 0..10_000 {
let s = format!("key{i}");
let (id, _) = interner.intern(&s);
assert_eq!(interner.lookup(id), Some(&*Box::leak(s.into_boxed_str())));
}
assert_eq!(interner.entry_count(), 10_000);
}
#[test]
fn string_ptr_roundtrip() {
let interner = PermInterner::new();
let (id, _) = interner.intern("perm");
let ptr = interner.string_ptr(id);
assert_eq!(unsafe { (*ptr).as_str() }, "perm");
// 二次调用返回同一稳定指针(仅物化一次)。
assert_eq!(interner.string_ptr(id), ptr);
}
#[test]
fn single_char_ptr_idempotent() {
let a = single_char_ptr(b'a');
assert_eq!(unsafe { (*a).as_str() }, "a");
// 二次调用返回同一稳定指针(仅物化一次)。
assert_eq!(single_char_ptr(b'a'), a);
}
#[test]
fn single_char_table_full_ascii() {
// 全表 128 条目均可物化且内容为对应的单字符文本。
for b in 0u8..=127 {
let ptr = single_char_ptr(b);
assert_eq!(unsafe { (*ptr).as_str() }, (b as char).to_string());
}
}
#[test]
fn typeof_table_content_and_stability() {
// 8 个 typeof 结果串与下标约定一一对应,且二次调用返回同一稳定指针。
let texts = ["undefined", "object", "boolean", "number", "string", "symbol", "bigint", "function"];
for (i, t) in texts.iter().enumerate() {
let ptr = typeof_string_ptr(i as u8);
assert_eq!(unsafe { (*ptr).as_str() }, *t);
assert_eq!(typeof_string_ptr(i as u8), ptr, "下标 {i} 二次调用应返回同一指针");
}
}
#[test]
fn encode_key_identity_and_escapes() {
// 良形且无 FFFD(含超平面良配对)编码为恒等文本。
assert_eq!(encode_key(&"abc🚀".encode_utf16().collect::<Vec<u16>>()), "abc🚀");
// 孤立 surrogate:FFFD + 4 位小写十六进制。
assert_eq!(encode_key(&[0xD800]), "\u{FFFD}d800");
assert_eq!(encode_key(&[0xDFFF]), "\u{FFFD}dfff");
assert_eq!(encode_key(&[0xDBFF, 0x61]), "\u{FFFD}dbffa");
// FFFD 单元:FFFD + 字面 "fffd"。
assert_eq!(encode_key(&[0xFFFD]), "\u{FFFD}fffd");
// 两种不同单元序列的键文本互异(解码可区分)。
assert_ne!(encode_key(&[0xFFFD]), encode_key(&[0xD800]));
// 文本 [FFFD,'d','8','0','0'] 与单元 [D800] 的键必须可区分(防物化串键)。
assert_ne!(encode_key(&[0xFFFD, 0x64, 0x38, 0x30, 0x30]), encode_key(&[0xD800]));
}
#[test]
fn encode_decode_roundtrip() {
let cases: Vec<Vec<u16>> = vec![
Vec::new(),
"a".encode_utf16().collect(),
"abc🚀😀".encode_utf16().collect(),
vec![0xD800, 0x42, 0xDC00],
vec![0xFFFD, 0xFFFD, 0x41, 0xFFFD, 0x66, 0x66, 0x66, 0x64],
vec![0xFFFF, 0x0041, 0xDBFF, 0xDC00, 0xD800],
];
for units in &cases {
let key = encode_key(units);
assert_eq!(decode_key(&key), units.as_slice(), "roundtrip 失败: key={key:?}");
}
}
#[test]
fn decode_key_defensive_raw_fffd() {
// 裸 FFFD(文本末尾或后跟非转义文本)还原为 FFFD 单元,后续字符不被吞。
assert_eq!(decode_key("a\u{FFFD}"), &[0x61, 0xFFFD]);
assert_eq!(decode_key("a\u{FFFD}zz"), &[0x61, 0xFFFD, 0x7A, 0x7A]);
}
#[test]
fn source_escape_marker_forms() {
// 孤立 surrogate / FFFD → 源码域 \u 转义形态;配对与 BMP 恒等。
assert_eq!(source_escape(&[0xD800]), "\\ud800");
assert_eq!(source_escape(&[0xDFFF]), "\\udfff");
assert_eq!(source_escape(&[0xFFFD]), "\\ufffd");
assert_eq!(source_escape(&[0x61, 0xD800, 0x62]), "a\\ud800b");
assert_eq!(source_escape(&[0xD83D, 0xDE00]), "\u{1F600}");
}
/// 源码是 JS 程序,反斜杠为语法字符:原样透传,转义文本形态逐字保留
/// (正则/字符串字面量内 `\u{1d306}`、`\\` 等序列不得被改写)。
#[test]
fn source_escape_backslash_passthrough() {
// 正则字面量源文本(u 模式 \u{..} 转义):逐字恒等。
let re_src: Vec<u16> = "/\\u{1d306}/u".encode_utf16().collect();
assert_eq!(source_escape(&re_src), "/\\u{1d306}/u");
// 转义文本(反斜杠 + "ud800" 字面文本)与孤立 surrogate 单元同形:
// 源码域中 `\ud800` 文本即孤立 surrogate 的源码表示,两者编码同一
// 源码文本是设计使然(源码即程序,oxc 只见转义文本)。
let text: Vec<u16> = "\\ud800".encode_utf16().collect();
assert_eq!(source_escape(&text), "\\ud800");
assert_eq!(source_escape(&[0xD800]), source_escape(&text));
// 双反斜杠(字面量内转义反斜杠)恒等。
let dbl: Vec<u16> = "\\\\".encode_utf16().collect();
assert_eq!(source_escape(&dbl), "\\\\");
}
/// 键域闭环:`decode_key ∘ source_escape_to_key ∘ source_escape` 对单元
/// 序列恒等(注入 marker 还原、用户转义文本透传、双反斜杠与 astral 对)。
#[test]
fn source_escape_roundtrip_via_key() {
let cases: Vec<Vec<u16>> = vec![
vec![0xD800],
vec![0xDFFF],
vec![0xFFFD],
vec![0x5C],
// 用户字面转义文本(非 marker 值域 / \u{..} 形态):逐字透传。
vec![0x5C, 0x75, 0x30, 0x30, 0x34, 0x31],
vec![0x5C, 0x75, 0x7B, 0x31, 0x64, 0x33, 0x30, 0x36, 0x7D],
vec![0x5C, 0x5C],
// 数据反斜杠紧邻注入 marker(`\\ud800`/`\\ufffd` 拼接形态)。
vec![0x5C, 0xD800],
vec![0x5C, 0xFFFD],
// FFFD 单元后跟 "d800" 文本:marker 消费恰 4 字符,不吞后续。
vec![0xFFFD, 0x64, 0x38, 0x30, 0x30],
vec![0x66, 0x66, 0x66, 0x64],
vec![0xD83D, 0xDE00],
];
for units in &cases {
let escaped = source_escape(units);
let key = source_escape_to_key(&escaped);
assert_eq!(&decode_key(&key), units.as_slice(), "roundtrip 失败: escaped={escaped:?}");
}
}
#[test]
fn string_ptr_materializes_units() {
let interner = PermInterner::new();
// 良形键:物化内容与旧行为逐位一致,且二次调用返回同一稳定指针。
let (id, _) = interner.intern("perm");
let ptr = interner.string_ptr(id);
assert_eq!(unsafe { (*ptr).as_str() }, "perm");
assert_eq!(interner.string_ptr(id), ptr);
// 含转义的键:物化出孤立 surrogate 单元形态。
let (id2, _) = interner.intern(&encode_key(&[0xD800]));
let ptr2 = interner.string_ptr(id2);
assert!(unsafe { (*ptr2).has_lone_surrogate() });
assert_eq!(unsafe { (*ptr2).units() }, &[0xD800][..]);
}
#[test]
fn perm_table_concurrent_first_use_unique_ptr() {
// 并发首用竞态回归:96 线程同一时刻命中冷表槽,强制多线程同入慢路径。
// 所有调用方必须拿到同一稳定指针,且该指针恒指向存活的 JsString。
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
const N: usize = 96;
let go = Arc::new(AtomicBool::new(false));
let mut handles = Vec::with_capacity(N);
for _ in 0..N {
let go = Arc::clone(&go);
handles.push(std::thread::spawn(move || {
while !go.load(Ordering::Acquire) {
std::hint::spin_loop();
}
// 裸指针 !Send,跨线程以 usize 传递,出口再转回。
let (i, c, t) = (small_int_ptr(42).unwrap(), single_char_ptr(b'x'), typeof_string_ptr(1));
(i as usize, c as usize, t as usize)
}));
}
go.store(true, Ordering::Release);
let mut results = Vec::with_capacity(N);
for h in handles {
results.push(h.join().expect("竞态线程"));
}
let (i0, c0, t0) = results[0];
for (i, c, t) in &results[1..] {
assert_eq!(*i, i0, "小整数表首用应全局唯一指针");
assert_eq!(*c, c0, "单字符表首用应全局唯一指针");
assert_eq!(*t, t0, "typeof 表首用应全局唯一指针");
}
// 返回指针必须指向存活 JsString:内容与长度可回读。
assert_eq!(unsafe { (*(i0 as *const JsString)).as_str() }, "42");
assert_eq!(unsafe { (*(c0 as *const JsString)).as_str() }, "x");
let t0 = t0 as *const JsString;
assert_eq!(unsafe { (*t0).as_str() }, "object");
assert_eq!(unsafe { (*t0).utf16_len() }, 6);
}