use std::sync::Arc; use oxide_compiler::compiler::Compiler; use oxide_runtime_api::VmHost; 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(&Arc::new(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 (_vm, 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 (_vm, 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 (_vm, 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 (_vm, 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 (_vm, 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}"#); } // --- 顶层 replacer 序(replacer 先于 undefined/函数/Symbol 早返)--- // 顶层 undefined:replacer 先被调用,返回值即序列化结果。 #[test] fn stringify_top_level_undefined_replacer_first() { let out = eval_str("JSON.stringify(undefined, (k,v) => 42)").unwrap(); assert_eq!(out, "42"); } // 顶层函数/Symbol:replacer 可替换为可序列化值。 #[test] fn stringify_top_level_fn_symbol_replacer() { let out = eval_str("JSON.stringify(function(){}, (k,v) => 'x')").unwrap(); assert_eq!(out, r#""x""#); let out = eval_str("JSON.stringify(Symbol(), (k,v) => 'y')").unwrap(); assert_eq!(out, r#""y""#); } // 顶层 undefined 无 replacer / replacer 透传:仍 undefined(守卫)。 #[test] fn stringify_top_level_undefined_still_undefined() { let (_vm, result) = eval("JSON.stringify(undefined, (k,v) => v)").unwrap(); assert!(result.is_undefined()); let (_vm, result) = eval("JSON.stringify(undefined)").unwrap(); assert!(result.is_undefined()); } // --- space --- #[test] fn space_number_indent() { let (_vm, 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 (_vm, result) = eval(r#"JSON.stringify({a:1}, null, -5)"#).unwrap(); assert_eq!(stringify_val(&result), r#"{"a":1}"#); } // space = +∞ → 10 空格 gap(ToIntegerOrInfinity 后 min(10, x))。 #[test] fn stringify_space_infinity_ten_spaces() { let out = eval_str("JSON.stringify({a:1}, null, Infinity)").unwrap(); assert_eq!(out, "{\n \"a\": 1\n}"); } // space = −∞/NaN → 空 gap。 #[test] fn stringify_space_neg_inf_nan_empty() { let out = eval_str("JSON.stringify({a:1}, null, -Infinity)").unwrap(); assert_eq!(out, r#"{"a":1}"#); let out = eval_str("JSON.stringify({a:1}, null, NaN)").unwrap(); assert_eq!(out, r#"{"a":1}"#); } // space 小数截断(2.5 → 2 空格)、12 钳 10。 #[test] fn stringify_space_fractional_and_clamp() { let out = eval_str("JSON.stringify({a:1}, null, 2.5)").unwrap(); assert_eq!(out, "{\n \"a\": 1\n}"); let out = eval_str("JSON.stringify({a:1}, null, 12)").unwrap(); assert_eq!(out, "{\n \"a\": 1\n}"); } // 装箱 Number space:ToNumber 后与素形同语义(+∞ → 10、−∞ → 空)。 #[test] fn stringify_space_boxed_number() { let out = eval_str("JSON.stringify({a:1}, null, new Number(Infinity))").unwrap(); assert_eq!(out, "{\n \"a\": 1\n}"); let out = eval_str("JSON.stringify({a:1}, null, new Number(-Infinity))").unwrap(); assert_eq!(out, r#"{"a":1}"#); } // space 字符串 astral:按前 10 码元截断(12 码元 → 5 对,非标量值 6 口径)。 #[test] fn stringify_space_astral_truncated_units() { let out = eval_str("JSON.stringify([1], null, '😀😀😀😀😀😀')").unwrap(); assert_eq!(out, "[\n😀😀😀😀😀1\n]"); } // 装箱 String space:同款码元口径。 #[test] fn stringify_space_astral_boxed_string() { let out = eval_str("JSON.stringify([1], null, new String('😀😀😀😀😀😀'))").unwrap(); assert_eq!(out, "[\n😀😀😀😀😀1\n]"); } // 截断落在代理对边界:gap 保留原始孤立 surrogate(node 口径,非 \u 转义)。 #[test] fn stringify_space_astral_lone_surrogate() { let (vm, result) = eval("JSON.stringify([1], null, '\\uD83D\\uD83D\\uD83D\\uD83D\\uD83D\\uD83D\\uD83D\\uD83D')").unwrap(); let units = vm.string_units(result); let expected: Vec = vec![ 0x5B, 0x0A, 0xD83D, 0xD83D, 0xD83D, 0xD83D, 0xD83D, 0xD83D, 0xD83D, 0xD83D, 0x31, 0x0A, 0x5D, ]; assert_eq!(units.as_ref(), expected.as_slice()); } // --- toJSON --- #[test] fn tojson_called_before_serialize() { let (_vm, 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 (_vm, 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 (_vm, 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 (_vm, 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 (_vm, 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 (_vm, 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 (_vm, result) = eval(r#"JSON.parse('{"a":1}')"#).unwrap(); assert!(result.is_object()); } // --- accessor 自身属性值读(SerializeJSONProperty 步 2 Get)--- // reverse arraylike + length accessor:getter 被触发,length 键保留在输出。 #[test] fn stringify_reverse_arraylike_length_accessor() { let (_vm, result) = eval( "(() => { const o = {0:'x',1:'y',length:2}; \ Object.defineProperty(o,'length',{get(){return 2;},set(v){},enumerable:true,configurable:true}); \ Array.prototype.reverse.call(o); return JSON.stringify(o); })()", ) .unwrap(); assert_eq!(stringify_val(&result), r#"{"0":"y","1":"x","length":2}"#); } // 对象属性访问器:字面量 getter 与 defineProperty 重定义 accessor 两形态。 #[test] fn stringify_object_accessor_getter() { let (_vm, result) = eval(r#"JSON.stringify({get x(){return 5;}, y:1})"#).unwrap(); assert_eq!(stringify_val(&result), r#"{"x":5,"y":1}"#); let (_vm, result) = eval( "(() => { const o = {x:1}; \ Object.defineProperty(o,'x',{get(){return 2;}}); return JSON.stringify(o); })()", ) .unwrap(); assert_eq!(stringify_val(&result), r#"{"x":2}"#); } // getter 的 this 绑定为属性所在对象。 #[test] fn stringify_accessor_this_binding() { let (_vm, result) = eval(r#"JSON.stringify({v:1, get p(){return this.v+10;}})"#).unwrap(); assert_eq!(stringify_val(&result), r#"{"v":1,"p":11}"#); } // 数组 replacer 白名单判定先于 Get:非白名单键的 getter 不触发。 #[test] fn stringify_whitelist_before_accessor_get() { let out = eval_str( "(() => { let fired = false; const o = {a:1, get b(){fired=true;return 2;}}; \ return JSON.stringify(o,['a']) + '|' + fired; })()", ) .unwrap(); assert_eq!(out, r#"{"a":1}|false"#); } // 数组元素访问器:元素位触发 getter,值为 getter 返回值。 #[test] fn stringify_array_element_accessor() { let out = eval_str( "(() => { const a = [1,2]; \ Object.defineProperty(a,0,{get(){return 9;},configurable:true}); \ Object.defineProperty(a,1,{get(){return 2;},configurable:true}); \ return JSON.stringify(a); })()", ) .unwrap(); assert_eq!(out, "[9,2]"); } // getter 返回值形态:function/undefined 省略,带 toJSON 的对象走钩子,setter-only 省略。 #[test] fn stringify_accessor_return_shapes() { let out = eval_str( "(() => { const o = {}; \ Object.defineProperty(o,'f',{get(){ return function g(){return 7;}; },enumerable:true,configurable:true}); \ Object.defineProperty(o,'u',{get(){ return undefined; },enumerable:true,configurable:true}); \ Object.defineProperty(o,'t',{get(){ return {toJSON(){return 7;}}; },enumerable:true,configurable:true}); \ Object.defineProperty(o,'s',{set(v){},enumerable:true,configurable:true}); \ return JSON.stringify(o); })()", ) .unwrap(); assert_eq!(out, r#"{"t":7}"#); } // space 缩进 + 访问器:getter 值正常入输出(判别 p 键存在;对象开括号换行 // 位置是 space 格式面 pre-existing 偏差,不属本钉面)。 #[test] fn stringify_space_with_accessor() { let (_vm, result) = eval( "(() => { const o = {}; \ Object.defineProperty(o,'p',{get(){return [1];},enumerable:true,configurable:true}); \ return JSON.stringify({o},null,2); })()", ) .unwrap(); let s = stringify_val(&result); assert!(s.contains(r#""p": ["#), "getter value missing from indented output: {}", s); assert!(s.contains("1"), "getter value absent: {}", s); } // getter 抛非 Error 原始值:原值传播(捕获侧见 number 42,非 TypeError 对象)。 #[test] fn stringify_accessor_throws_original_value() { let out = eval_str( "(() => { try { JSON.stringify({get x(){throw 42;}, y:1}); } \ catch (e) { return typeof e + '|' + e; } })()", ) .unwrap(); assert_eq!(out, "number|42"); } // toJSON 抛非 Error 原始值:原值传播。 #[test] fn stringify_tojson_throws_original_value() { let out = eval_str( "(() => { try { JSON.stringify({toJSON(){throw 42;}}); } \ catch (e) { return typeof e + '|' + e; } })()", ) .unwrap(); assert_eq!(out, "number|42"); } // replacer 抛非 Error 原始值:原值传播。 #[test] fn stringify_replacer_throws_original_value() { let out = eval_str( "(() => { try { JSON.stringify({a:1}, function(k,v){ if(k==='a') throw 'boom'; return v; }); } \ catch (e) { return typeof e + '|' + e; } })()", ) .unwrap(); assert_eq!(out, "string|boom"); } // 嵌套序:toJSON 先于 replacer 应用,replacer 见到 toJSON 结果。 #[test] fn stringify_nested_tojson_before_replacer() { let out = eval_str( "(() => { let seen; const inner = {toJSON(){return 'T';}}; \ const s = JSON.stringify({a:inner}, function(k,v){ if(k==='a') seen = String(v); return v; }); \ return seen + '|' + s; })()", ) .unwrap(); assert_eq!(out, r#"T|{"a":"T"}"#); } // 顶层 toJSON 结果自身带 toJSON:只应用一次。 #[test] fn stringify_top_level_tojson_once() { let (_vm, result) = eval(r#"JSON.stringify({toJSON(){return {toJSON(){return 'inner';}};}})"#).unwrap(); assert_eq!(stringify_val(&result), r#"{}"#); } // 访问器返回自身成环:getter 触发后环检抛 TypeError。 #[test] fn stringify_accessor_self_cycle() { let result = eval( "(() => { const o = {n:1}; \ Object.defineProperty(o,'self',{get(){return o;},enumerable:true,configurable:true}); \ return JSON.stringify(o); })()", ); match result { Err(e) => assert!(e.to_lowercase().contains("circular"), "expected circular error, got: {}", e), Ok(_) => panic!("expected cycle error"), } } // 不可枚举访问器:省略(守卫,防修后翻红)。 #[test] fn stringify_non_enumerable_accessor_skipped() { let out = eval_str( "(() => { const o = {v:2}; \ Object.defineProperty(o,'h',{get(){return 99;},enumerable:false,configurable:true}); \ return JSON.stringify(o); })()", ) .unwrap(); assert_eq!(out, r#"{"v":2}"#); } // 嵌套 toJSON 结果为对象:单应用下正常展开(守卫)。 #[test] fn stringify_nested_tojson_result_object() { let (_vm, result) = eval(r#"JSON.stringify({a:{toJSON(){return {};}}})"#).unwrap(); assert_eq!(stringify_val(&result), r#"{"a":{}}"#); } // 数组附加命名属性:序列化省略(守卫)。 #[test] fn stringify_array_ignores_named_props() { let out = eval_str("(() => { const a = [1,2]; a.x = 'hello'; return JSON.stringify(a); })()").unwrap(); assert_eq!(out, "[1,2]"); } // --- BigInt 值位(SerializeJSONProperty 步 10 抛 TypeError;步 2 查 toJSON)--- fn assert_bigint_serialize_err(source: &str) { let result = eval(source); match result { Err(e) => { assert!( e.to_lowercase().contains("do not know how to serialize a bigint"), "expected BigInt serialize TypeError, got: {}", e ) } Ok(_) => panic!("expected BigInt serialize TypeError, got ok: {}", source), } } // 顶层原语四值位:1n / -1n / 0n / -0n 恒抛,无文本产出。 #[test] fn stringify_bigint_top_level_throws() { assert_bigint_serialize_err("JSON.stringify(1n)"); assert_bigint_serialize_err("JSON.stringify(-1n)"); assert_bigint_serialize_err("JSON.stringify(0n)"); assert_bigint_serialize_err("JSON.stringify(-0n)"); } // 大位宽 BigInt 同样抛错(不产十进制文本)。 #[test] fn stringify_bigint_wide_bits_throws() { assert_bigint_serialize_err("JSON.stringify(2n ** 32n + 1n)"); } // 嵌套对象属性与数组元素位(含深嵌套、稀疏)均经值位臂抛错。 #[test] fn stringify_bigint_nested_throws() { assert_bigint_serialize_err("JSON.stringify({a:1n})"); assert_bigint_serialize_err("JSON.stringify([1n,2n,[3n]])"); assert_bigint_serialize_err("JSON.stringify({a:{b:1n}})"); assert_bigint_serialize_err("JSON.stringify([,1n])"); } // 空格缩进不改变抛错路径。 #[test] fn stringify_bigint_space_indent_throws() { assert_bigint_serialize_err("JSON.stringify({a:1n}, null, 2)"); } // replacer 白名单命中键后仍走值位抛错。 #[test] fn stringify_bigint_whitelist_throws() { assert_bigint_serialize_err("JSON.stringify({a:1n}, ['a'])"); } // 装箱 BigInt(步 4d 无条件解包 [[BigIntData]])与裸值同抛。 #[test] fn stringify_bigint_boxed_throws() { assert_bigint_serialize_err("JSON.stringify(Object(0n))"); assert_bigint_serialize_err("JSON.stringify({a:Object(1n)})"); } // BigInt64Array 元素位经对象臂读值后在值位臂抛错。 #[test] fn stringify_bigint64array_throws() { assert_bigint_serialize_err("JSON.stringify(new BigInt64Array([1n,2n]))"); } // replacer 透传不改变值位;replacer 结果再入值位臂同样抛错。 #[test] fn stringify_bigint_replacer_throws() { assert_bigint_serialize_err("JSON.stringify(1n, (k,v) => v)"); assert_bigint_serialize_err("JSON.stringify({a:1n}, (k,v) => v)"); assert_bigint_serialize_err("JSON.stringify({a:1}, (k,v) => 2n)"); } // BigInt.prototype 挂数据属性 toJSON:钩子被触发,返回串值正常序列化。 #[test] fn stringify_bigint_tojson_hook_top_level() { let out = eval_str( "(() => { BigInt.prototype.toJSON = function(){ return this.toString(); }; \ const s = JSON.stringify(0n); \ delete BigInt.prototype.toJSON; \ return s; })()", ) .unwrap(); assert_eq!(out, r#""0""#); } // toJSON 钩子嵌套对象属性位:receiver 为 BigInt 原语,this.toString() 生效。 #[test] fn stringify_bigint_tojson_hook_nested() { let out = eval_str( "(() => { BigInt.prototype.toJSON = function(){ return this.toString(); }; \ const s = JSON.stringify({a:1n}); \ delete BigInt.prototype.toJSON; \ return s; })()", ) .unwrap(); assert_eq!(out, r#"{"a":"1"}"#); } // 守卫:replacer 先于值位转换 BigInt → number,不抛错。 #[test] fn stringify_bigint_replacer_transform_guard() { let out = eval_str("JSON.stringify({b:1n}, function(k,v){ if(typeof v==='bigint') return Number(v); return v; })") .unwrap(); assert_eq!(out, r#"{"b":1}"#); } // --- reviver 抛错原值传播(InternalizeJSONProperty 步 3)--- // reviver 抛非 Error 原语:原值传播(捕获侧见 number 42,非 TypeError 对象)。 #[test] fn parse_reviver_throws_original_value() { let out = eval_str( "(() => { try { JSON.parse('0', function(){ throw 42; }); } \ catch (e) { return typeof e + '|' + e; } })()", ) .unwrap(); assert_eq!(out, "number|42"); } // reviver 抛字符串:原始串传播。 #[test] fn parse_reviver_throws_string() { let out = eval_str( "(() => { try { JSON.parse('0', function(){ throw 'boom'; }); } \ catch (e) { return typeof e + '|' + e; } })()", ) .unwrap(); assert_eq!(out, "string|boom"); } // reviver 抛 Error 对象:原错误对象传播(种类与消息保真)。 #[test] fn parse_reviver_throws_range_error() { let out = eval_str( "(() => { try { JSON.parse('0', function(){ throw new RangeError('re'); }); } \ catch (e) { return e.constructor.name + '|' + e.message; } })()", ) .unwrap(); assert_eq!(out, "RangeError|re"); } // --- reviver 读写臂(Get 读臂 / [[Delete]] + CreateDataProperty 写臂 / 根级无写回)--- // 读臂 Get:reviver 删自身 b 后读值走原型链,CreateDataProperty 重建自身 b(对象臂)。 #[test] fn parse_reviver_object_get_prop_from_prototype() { let out = eval_str( "(() => { Object.prototype.b = 3; \ const obj = JSON.parse('{\"a\": 1, \"b\": 2}', function(key, value) { \ if (key === 'a') { delete this.b; } return value; }); \ delete Object.prototype.b; \ return [obj.a, obj.hasOwnProperty('b'), obj.b].join('|'); })()", ) .unwrap(); assert_eq!(out, "1|true|3"); } // 读臂 Get:数组臂同款(删自身 [1] 后原型读 3 重建)。 #[test] fn parse_reviver_array_get_prop_from_prototype() { let out = eval_str( "(() => { Array.prototype[1] = 3; \ const arr = JSON.parse('[1, 2]', function(key, value) { \ if (key === '0') { delete this[1]; } return value; }); \ delete Array.prototype[1]; \ return [arr[0], arr.hasOwnProperty('1'), arr[1]].join('|'); })()", ) .unwrap(); assert_eq!(out, "1|true|3"); } // 读臂 Get:自身访问器 getter 抛错穿透,原值传播(非 TypeError 折叠)。 #[test] fn parse_reviver_get_name_err_original_value() { let out = eval_str( "(() => { try { JSON.parse('[0,0]', function() { \ Object.defineProperty(this, '1', { get() { throw 42; } }); }); } \ catch (e) { return typeof e + '|' + e; } return 'nothrow'; })()", ) .unwrap(); assert_eq!(out, "number|42"); } // 写臂 CreateDataProperty:非可配置属性写败静默,保旧值(对象臂)。 #[test] fn parse_reviver_object_non_configurable_prop_create() { let out = eval_str( "(() => { const obj = JSON.parse('{\"a\": 1, \"b\": 2}', function(key, value) { \ if (key === 'a') { Object.defineProperty(this, 'b', {configurable: false}); } \ if (key === 'b') return 22; return value; }); \ return [obj.a, obj.b].join('|'); })()", ) .unwrap(); assert_eq!(out, "1|2"); } // 写臂 [[Delete]]:非可配置属性删败静默,属性保留(对象臂)。 #[test] fn parse_reviver_object_non_configurable_prop_delete() { let out = eval_str( "(() => { const obj = JSON.parse('{\"a\": 1, \"b\": 2}', function(key, value) { \ if (key === 'a') { Object.defineProperty(this, 'b', {configurable: false}); } \ if (key === 'b') return; return value; }); \ return [obj.a, obj.hasOwnProperty('b'), obj.b].join('|'); })()", ) .unwrap(); assert_eq!(out, "1|true|2"); } // 写臂同款两臂(数组臂)。 #[test] fn parse_reviver_array_non_configurable_prop_create() { let out = eval_str( "(() => { const arr = JSON.parse('[1, 2]', function(key, value) { \ if (key === '0') { Object.defineProperty(this, '1', {configurable: false}); } \ if (key === '1') return 22; return value; }); \ return [arr[0], arr[1]].join('|'); })()", ) .unwrap(); assert_eq!(out, "1|2"); } #[test] fn parse_reviver_array_non_configurable_prop_delete() { let out = eval_str( "(() => { const arr = JSON.parse('[1, 2]', function(key, value) { \ if (key === '0') { Object.defineProperty(this, '1', {configurable: false}); } \ if (key === '1') return; return value; }); \ return [arr[0], arr.hasOwnProperty('1'), arr[1]].join('|'); })()", ) .unwrap(); assert_eq!(out, "1|true|2"); } // 根级无写回:wrapper `''` 槽值/描述符恒保留(reviver 返 undefined 不抹槽)。 #[test] fn parse_reviver_wrapper_slot_preserved() { let out = eval_str( "(() => { let w; JSON.parse('2', function() { w = this; }); \ const d = Object.getOwnPropertyDescriptor(w, ''); \ return [w[''], Object.getOwnPropertyNames(w).length, d.writable, d.enumerable, d.configurable].join('|'); })()", ) .unwrap(); assert_eq!(out, "2|1|true|true|true"); } // 根返回值 = 根级 reviver 返回值原样。 #[test] fn parse_reviver_root_return_value() { let (_vm, result) = eval(r#"JSON.parse('5', function(k,v){return v*2})"#).unwrap(); assert!(result.is_int() || result.is_double(), "expected number"); if result.is_int() { assert_eq!(result.as_int(), 10); } else { assert_eq!(result.as_double() as i32, 10); } } // this 面守卫:根级 this = wrapper(`''` 槽即结果对象)、子级 this = 容器, // 前向改写 `this[1] = 99` 在子读点生效。 #[test] fn parse_reviver_this_binding_guard() { let out = eval_str( "(() => { let rootThis, childThis; \ const arr = JSON.parse('[1, 2]', function(key, value) { \ if (key === '') rootThis = this; \ if (key === '0') childThis = this; \ if (key === '0') this[1] = 99; return value; }); \ return [rootThis[''] === arr, childThis === arr, rootThis === childThis, arr[1]].join('|'); })()", ) .unwrap(); assert_eq!(out, "true|true|false|99"); } // --- PropertyList 列表序(SerializeJSONObject 步 5 K = P)--- // 顶层对象:输出序 = 白名单列表序(非自身枚举序)。 #[test] fn replacer_array_list_order() { let out = eval_str(r#"JSON.stringify({b:1,a:2,c:3}, ['c','b','a'])"#).unwrap(); assert_eq!(out, r#"{"c":3,"b":1,"a":2}"#); } // 嵌套层级每级同用列表序。 #[test] fn replacer_array_nested_list_order() { let out = eval_str(r#"JSON.stringify({a:{b:2,c:3}}, ['c','b','a'])"#).unwrap(); assert_eq!(out, r#"{"a":{"c":3,"b":2}}"#); } // TypedArray:列表序定输出序,整数索引名读元素。 #[test] fn replacer_array_typed_array_list_order() { let out = eval_str(r#"JSON.stringify(new Float64Array([1,2,3]), ['2','0','1'])"#).unwrap(); assert_eq!(out, r#"{"2":3,"0":1,"1":2}"#); } // --- 白名单元素强转(Stringify 步 4:装箱 String/Number 走 ToPrimitive)--- // 装箱 Number 自定义 toString:string hint 调用结果作键。 #[test] fn replacer_boxed_number_tostring() { let out = eval_str( "(() => { const n = new Number(10); \ n.toString = function(){ return 'toString'; }; \ return JSON.stringify({10:1,toString:2}, [n]); })()", ) .unwrap(); assert_eq!(out, r#"{"toString":2}"#); } // 素形装箱 Number:盒值 → "10"。 #[test] fn replacer_boxed_number_plain() { let out = eval_str(r#"JSON.stringify({10:1}, [new Number(10)])"#).unwrap(); assert_eq!(out, r#"{"10":1}"#); } // 素形装箱 String:盒值 → "str"。 #[test] fn replacer_boxed_string_plain() { let out = eval_str(r#"JSON.stringify({str:4}, [new String('str')])"#).unwrap(); assert_eq!(out, r#"{"str":4}"#); } // --- 白名单键 Get 语义(原型链 + 不可枚举自身 + 访问器)--- // 原型链 getter 键在白名单给定时命中(getter 触发)。 #[test] fn replacer_whitelist_proto_getter() { let out = eval_str( "(() => { const base = {get b(){ return 9; }}; \ const o = Object.create(base); o.a = 1; \ return JSON.stringify(o, ['a','b']); })()", ) .unwrap(); assert_eq!(out, r#"{"a":1,"b":9}"#); } // 不可枚举自身键在白名单给定时同样序列化。 #[test] fn replacer_whitelist_non_enumerable_own() { let out = eval_str( "(() => { const o = {a:1}; \ Object.defineProperty(o,'b',{value:2,enumerable:false,configurable:true}); \ return JSON.stringify(o, ['a','b']); })()", ) .unwrap(); assert_eq!(out, r#"{"a":1,"b":2}"#); } // 嵌套层级原型链 getter 同样命中。 #[test] fn replacer_whitelist_nested_proto_getter() { let out = eval_str( "(() => { const base = {get x(){ return 7; }}; \ const o = Object.create(base); o.y = 1; \ return JSON.stringify({o}, ['o','x','y']); })()", ) .unwrap(); assert_eq!(out, r#"{"o":{"x":7,"y":1}}"#); } // --- toJSON 查表 Get 语义(accessor 形 toJSON 触发)--- // BigInt.prototype toJSON accessor 形:getter 触发,receiver = BigInt 原语。 #[test] fn stringify_bigint_tojson_accessor() { let out = eval_str( "(() => { \"use strict\"; \ Object.defineProperty(BigInt.prototype, 'toJSON', \ {get(){ return () => typeof this; }, configurable:true}); \ const s = JSON.stringify(1n); \ delete BigInt.prototype.toJSON; \ return s; })()", ) .unwrap(); assert_eq!(out, r#""bigint""#); } // --- 守卫(修后零漂移)--- // 白名单缺失名:空对象输出。 #[test] fn replacer_whitelist_missing_name() { let out = eval_str(r#"JSON.stringify({b:1}, ['zzz'])"#).unwrap(); assert_eq!(out, "{}"); } // TA 白名单非索引名(length)经 Get 读命名键。 #[test] fn replacer_whitelist_typed_array_length() { let out = eval_str(r#"JSON.stringify(new Float64Array(2), ['length'])"#).unwrap(); assert_eq!(out, r#"{"length":2}"#); } // 数组容器不查白名单。 #[test] fn replacer_whitelist_array_container_ignores() { let out = eval_str(r#"JSON.stringify([1,2], ['1'])"#).unwrap(); assert_eq!(out, "[1,2]"); } // 装箱 Boolean / 普通对象元素跳过(不产键)。 #[test] fn replacer_whitelist_skip_object_elements() { let out = eval_str(r#"JSON.stringify({b:5}, [new Boolean(true), {custom:1}])"#).unwrap(); assert_eq!(out, "{}"); } // 白名单命中键值为 undefined:跳键。 #[test] fn replacer_whitelist_undefined_value_skipped() { let out = eval_str(r#"JSON.stringify({a:1,b:undefined}, ['a','b'])"#).unwrap(); assert_eq!(out, r#"{"a":1}"#); } // --- 嵌套深度上限(递归限制)--- // 深度 128 层恰好过:128 层嵌套数组正常解析。 #[test] fn parse_depth_128_ok() { let out = eval_str( "(() => { const v = JSON.parse('['.repeat(128) + ']'.repeat(128)); \ return String(Array.isArray(v) && v.length === 1); })()", ) .unwrap(); assert_eq!(out, "true"); } // 深度 129 层超限:抛 SyntaxError(递归限制),而非进程 abort。 #[test] fn parse_depth_129_throws() { let out = eval_str( "(() => { try { JSON.parse('['.repeat(129) + ']'.repeat(129)); return 'no-throw'; } \ catch (e) { return e.constructor.name + '|' + e.message; } })()", ) .unwrap(); assert_eq!(out, "SyntaxError|recursion limit exceeded"); } // 深度计数复位:深嵌套解析超限失败后,随后普通嵌套解析不受残留计数影响。 #[test] fn parse_depth_count_resets() { let out = eval_str( "(() => { try { JSON.parse('['.repeat(129) + ']'.repeat(129)); } catch (e) {} \ const v = JSON.parse('{\"a\":{\"b\":[1]}}'); \ return String(v.a.b[0]); })()", ) .unwrap(); assert_eq!(out, "1"); } fn eval_str(source: &str) -> Result { let (vm, result) = eval(source)?; vm.lookup_str(result) .ok_or_else(|| "completion value is not a string".to_string()) }