From 87d588a10e610c9ee61f45293b31202349b3e37d Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 20 Sep 2026 13:31:04 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20JSON=20parse=20reviver=20=E8=AF=BB?= =?UTF-8?q?=E5=86=99=E8=87=82=E6=8C=89=E6=AD=A3=E5=85=B8=E5=BD=92=E4=BD=8D?= =?UTF-8?q?=EF=BC=8C=E6=A0=B9=E7=BA=A7=E4=B8=8D=E5=86=99=E5=9B=9E=20wrappe?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/json.rs | 83 ++++++----- .../tests/builtins_json_tests.rs | 133 ++++++++++++++++++ 2 files changed, 181 insertions(+), 35 deletions(-) diff --git a/crates/oxide_builtins/src/json.rs b/crates/oxide_builtins/src/json.rs index 6dfe2242..661dfdc2 100644 --- a/crates/oxide_builtins/src/json.rs +++ b/crates/oxide_builtins/src/json.rs @@ -33,7 +33,8 @@ pub fn json_parse(vm: &mut H, args: &[u8]) -> NativeResult { let mut result = value_to_jsvalue(vm, &parsed); // reviver 遍历以 holder 包装对象为根:解析得到的根值存于空串键槽, - // 后序遍历自该槽展开,重建完成后以槽内最终值作为返回值。 + // 后序遍历自该槽展开。返回值 = 根级 reviver 返回值原样(wrapper 的 + // `''` 槽由父级循环施加,根级无父级,槽恒不被触碰)。 if args.len() > 2 { let reviver_val = vm.reg(args[2]); if reviver_val.is_object() { @@ -42,14 +43,10 @@ pub fn json_parse(vm: &mut H, args: &[u8]) -> NativeResult { let empty_si = vm.kernel_core().perm_interner().intern("").0; let holder = create_wrapper(vm, result); let holder_ptr = holder.as_js_object_ptr(); - match walk_reviver(vm, holder_ptr, empty_si, reviver_val) { - Ok(()) => { - let holder_obj = unsafe { &*holder_ptr }; - let final_val = holder_obj.get_prop_at(0u32); - result = if final_val.is_undefined() { JsValue::undefined() } else { final_val }; - } + result = match walk_reviver(vm, holder_ptr, empty_si, reviver_val) { + Ok(new_val) => new_val, Err(e) => return NativeResult::Err(e), - } + }; } } } @@ -57,51 +54,54 @@ pub fn json_parse(vm: &mut H, args: &[u8]) -> NativeResult { NativeResult::Ok(result) } +/// InternalizeJSONProperty 后序遍历:Get 读当前值 → 递归子节点并把每子返回值 +/// 施加于容器(undefined → [[Delete]],否则 CreateDataProperty,两失败路径 +/// 静默)→ 调用 reviver。返回值 = 本级 reviver 返回值原样,由父级循环施加 +/// (根级由 json_parse 直接作 parse 结果)。 fn walk_reviver( vm: &mut H, holder_ptr: *mut JsObject, key_si: u32, reviver: JsValue, -) -> Result<(), JsValue> { - let holder = unsafe { &*holder_ptr }; - let slot = vm.get_own_property_slot(holder, key_si); - let pos = match slot { - Some(p) => p, - None => return Ok(()), - }; - let mut val = holder.get_prop_at(pos); +) -> Result { + let holder_val = JsValue::from_js_object(holder_ptr); - // 后序遍历:先递归处理子节点。 + // 读臂:完整 Get(原型链 + 自身访问器触发),getter 抛出传播原值。 + let val = match vm.ordinary_get(unsafe { &*holder_ptr }, key_si, holder_val) { + Ok(v) => v, + Err(msg) => { + let exc = vm + .take_uncaught_value() + .unwrap_or_else(|| crate::error::create_type_error(vm, &msg)); + return Err(exc); + } + }; + + // 后序遍历:先递归子节点,每子返回值施加于容器。 if val.is_object() { let obj_ptr = val.as_js_object_ptr(); if !obj_ptr.is_null() { - let obj = unsafe { &*obj_ptr }; - if obj.is_array() { - let len = obj.prop_count() as usize; + if unsafe { (*obj_ptr).is_array() } { + let len = unsafe { (*obj_ptr).prop_count() } as usize; for i in 0..len { let child_si = make_int_key(i as u32); - walk_reviver(vm, obj_ptr, child_si, reviver)?; + let new_val = walk_reviver(vm, obj_ptr, child_si, reviver)?; + apply_child_result(vm, obj_ptr, child_si, new_val); } - // 子节点可能已改写父级,重新读取当前值。 - val = holder.get_prop_at(pos); } else { - let keys = walk_own_keys(vm, obj); + let keys = { + let obj = unsafe { &*obj_ptr }; + walk_own_keys(vm, obj) + }; for (child_si, _child_pos) in keys { - walk_reviver(vm, obj_ptr, child_si, reviver)?; + let new_val = walk_reviver(vm, obj_ptr, child_si, reviver)?; + apply_child_result(vm, obj_ptr, child_si, new_val); } - // 子节点可能已改写父级,重新读取当前值。 - val = holder.get_prop_at(pos); } } } - // 对当前值调用 reviver,用返回值覆盖属性槽。 + // 对当前值调用 reviver,返回值上抛由父级施加。 let key_val = crate::object::key_si_to_js_value(vm, key_si); - let holder_val = JsValue::from_js_object(holder_ptr); match vm.call_function_sync(reviver, holder_val, &[key_val, val]) { - Ok(new_val) => { - unsafe { - (*holder_ptr).set_prop_at(pos, new_val); - } - Ok(()) - } + Ok(new_val) => Ok(new_val), Err(msg) => { // reviver 内抛出的原始值原样传播(不折叠为 TypeError 文本)。 let exc = vm @@ -112,6 +112,19 @@ fn walk_reviver( } } +/// 子节点递归返回值施加于容器:`undefined` → [[Delete]](非可配置保留), +/// 否则 CreateDataProperty(非可配置静默保旧值,新键建自身)。两失败路径 +/// 均静默不抛(规范明注)。 +fn apply_child_result(vm: &mut H, child_ptr: *mut JsObject, child_si: u32, new_val: JsValue) { + if new_val.is_undefined() { + let _ = crate::object::delete_own_property(vm, unsafe { &mut *child_ptr }, child_si); + } else { + let _ = vm.define_data_property( + unsafe { &mut *child_ptr }, child_si, new_val, PropAttributes::new(true, true, true), + ); + } +} + fn value_to_jsvalue(vm: &mut H, val: &serde_json::Value) -> JsValue { match val { serde_json::Value::Null => JsValue::null(), diff --git a/crates/oxide_builtins/tests/builtins_json_tests.rs b/crates/oxide_builtins/tests/builtins_json_tests.rs index 1a702398..cc1e8105 100644 --- a/crates/oxide_builtins/tests/builtins_json_tests.rs +++ b/crates/oxide_builtins/tests/builtins_json_tests.rs @@ -464,6 +464,139 @@ fn parse_reviver_throws_range_error() { 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)--- // 顶层对象:输出序 = 白名单列表序(非自身枚举序)。