This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_builtins/tests/builtins_json_tests.rs
T
pjh123 f2535ffc77 perf: 字符串 +/+= 二元拼接改 Cons rope 惰性扁平化消除 O(n²)
JsString 拆 Flat/Cons 变体:Cons 经独立 ConsNode 保存左右子节点 + O(1)
字节长 + 惰性扁平化产物缓存(OnceLock 原子发布,产物挂节点生命周期)。
concat_strings 接线:总长 ≤ 128B 的小链急切扁平(与 rope 前同款,拼接即
消费场景零开销),大链 O(1) 链接——大段重复拼接从 O(n²) 拷贝降为 O(n)
链接,文本在消费点(.length/==/string_ref/方法族)首次扁平化后缓存。

GC 兼容:mark 收口 mark_string_live 传播 Cons 子节点与产物闭包(显式栈
防深链爆栈);sweep/full_reset 共用 drop 辅助连带释放产物;节点字节记账
用 O(1) byte_len。rope 节点登记 session 字符串表,与执行期字符串 GC
(指令边界触发)天然兼容。JsString 结构 32B 与 rope 前同 bin,小串路径
零结构回退;unsafe impl Send/Sync 论证同 StringPtr(perm 恒 Flat)。
2026-08-15 14:32:08 +08:00

141 lines
4.2 KiB
Rust

use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<(Vm, JsValue), String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
let mut vm = Vm::new();
let result = vm.run(&module)?;
Ok((vm, result))
}
fn stringify_val(val: &JsValue) -> String {
if val.is_string() {
unsafe { (*val.as_string_ptr()).to_owned_string() }
} else {
format!("{:?}", val)
}
}
// --- replacer array ---
#[test]
fn replacer_array_filters_properties() {
let (_, result) = eval(r#"JSON.stringify({a:1,b:2,c:3}, ['a','c'])"#).unwrap();
assert_eq!(stringify_val(&result), r#"{"a":1,"c":3}"#);
}
#[test]
fn replacer_array_numeric_keys() {
let (_, result) = eval(r#"JSON.stringify({a:1,b:2}, ['b'])"#).unwrap();
assert_eq!(stringify_val(&result), r#"{"b":2}"#);
}
// --- replacer function ---
#[test]
fn replacer_function_skip_property() {
let (_, result) =
eval(r#"JSON.stringify({a:1,b:2}, function(k,v){if(k==='a')return undefined;return v})"#).unwrap();
assert_eq!(stringify_val(&result), r#"{"b":2}"#);
}
#[test]
fn replacer_function_array_null() {
let (_, result) = eval(r#"JSON.stringify([1,2,3], function(k,v){if(k==='1')return undefined;return v})"#).unwrap();
assert_eq!(stringify_val(&result), "[1,null,3]");
}
#[test]
fn replacer_function_transform() {
let (_, result) =
eval(r#"JSON.stringify({a:1}, function(k,v){if(typeof v==='number')return v*2;return v})"#).unwrap();
assert_eq!(stringify_val(&result), r#"{"a":2}"#);
}
// --- space ---
#[test]
fn space_number_indent() {
let (_, result) = eval(r#"JSON.stringify({a:1}, null, 2)"#).unwrap();
let s = stringify_val(&result);
assert!(s.len() > 5, "expected indented output, got: {}", s);
assert!(s.starts_with('{'), "should start with brace");
}
#[test]
fn space_negative_clamped() {
let (_, result) = eval(r#"JSON.stringify({a:1}, null, -5)"#).unwrap();
assert_eq!(stringify_val(&result), r#"{"a":1}"#);
}
// --- toJSON ---
#[test]
fn tojson_called_before_serialize() {
let (_, result) = eval(r#"JSON.stringify({toJSON:function(){return {x:1}}})"#).unwrap();
assert_eq!(stringify_val(&result), r#"{"x":1}"#);
}
#[test]
fn tojson_non_callable_ignored() {
let (_, result) = eval(r#"JSON.stringify({toJSON:'not-a-function', a:1})"#).unwrap();
// toJSON 属性是字符串,应按普通属性序列化。
assert!(stringify_val(&result).contains("toJSON"));
assert!(stringify_val(&result).contains(r#""a":1"#));
}
// --- cycle detection ---
#[test]
fn cycle_throws_type_error() {
let result = eval("var a={};a.self=a;JSON.stringify(a)");
match result {
Err(e) => assert!(e.to_lowercase().contains("circular"), "error should mention 'circular', got: {}", e),
Ok(_) => panic!("expected cycle error, got ok"),
}
}
#[test]
fn no_cycle_distinct_objects_ok() {
let (_, result) = eval(r#"JSON.stringify([{a:1},{a:1}])"#).unwrap();
assert_eq!(stringify_val(&result), r#"[{"a":1},{"a":1}]"#);
}
// --- reviver ---
#[test]
fn reviver_transform_values() {
let (_, result) =
eval(r#"JSON.parse('{"a":1,"b":2}', function(k,v){if(typeof v==='number')return v*2;return v})"#).unwrap();
assert!(result.is_object());
}
#[test]
fn reviver_delete_property() {
let (_, result) =
eval(r#"JSON.parse('{"a":1,"b":2}', function(k,v){if(k==='a')return undefined;return v})"#).unwrap();
// 属性被软删除(置为 undefined)。
// 为 test262 兼容,stringify 应省略 undefined 属性。
assert!(result.is_object());
}
#[test]
fn reviver_root_key_empty_string() {
let (_, result) = eval(r#"JSON.parse('42', function(k,v){return v+1})"#).unwrap();
assert!(result.is_int() || result.is_double(), "expected number");
if result.is_int() {
assert_eq!(result.as_int(), 43);
} else {
assert_eq!(result.as_double() as i32, 43);
}
}
#[test]
fn reviver_no_reviver_works() {
let (_, result) = eval(r#"JSON.parse('{"a":1}')"#).unwrap();
assert!(result.is_object());
}