diff --git a/crates/oxide_builtins/src/module.rs b/crates/oxide_builtins/src/module.rs index b8346459..fae23580 100644 --- a/crates/oxide_builtins/src/module.rs +++ b/crates/oxide_builtins/src/module.rs @@ -1175,4 +1175,50 @@ mod tests { let state = deferred_state(ns_obj).expect("deferred state 应存活"); assert_eq!(state.error.unwrap().as_js_object_ptr(), err_ptr, "error 指针应稳定"); } + + #[test] + fn deferred_ns_read_target_falls_back_for_non_object_namespace() { + let (mut vm, fn_val) = vm_with_function(); + // 模块函数返回非对象(42):求值成功后 [[Namespace]] 为非对象值。 + vm.set_reg(1, fn_val); + vm.set_reg(2, JsValue::undefined()); + let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + let ns_ptr = ns_val.as_js_object_ptr(); + + // 模拟求值完成:[[Evaluated]] 置真、[[Namespace]] 为非对象值 42。 + { + let ns_obj = unsafe { &*ns_ptr }; + let state = deferred_state_mut(ns_obj).expect("deferred state 应已安装"); + state.evaluated = true; + state.namespace = JsValue::int(42); + } + + // 权威读目标回落对象自身(预注册占位槽),非对象 [[Namespace]] 不作读目标。 + let ns_obj = unsafe { &*ns_ptr }; + let target = deferred_ns_read_target(ns_obj); + assert!( + std::ptr::eq(target as *const JsObject, ns_ptr as *const JsObject), + "非对象 [[Namespace]] 应回落对象自身" + ); + } + + #[test] + fn deferred_ns_read_target_returns_real_ns_for_object_namespace() { + let (mut vm, fn_val) = vm_with_function(); + // 真实 ns 对象(求值成功且 [[Namespace]] 为对象,mixed 形态)。 + let real_ns = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + let real_ns_val = JsValue::from_js_object(real_ns); + vm.set_reg(1, fn_val); + vm.set_reg(2, real_ns_val); + let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap(); + let ns_ptr = ns_val.as_js_object_ptr(); + + // 权威读目标返回真实 ns(活值与真实槽位在真实 ns 条目表)。 + let ns_obj = unsafe { &*ns_ptr }; + let target = deferred_ns_read_target(ns_obj); + assert!( + std::ptr::eq(target as *const JsObject, real_ns as *const JsObject), + "对象 [[Namespace]] 应返回真实 ns" + ); + } } diff --git a/crates/oxide_vm/src/vm_props.rs b/crates/oxide_vm/src/vm_props.rs index b5281990..bb084ea3 100644 --- a/crates/oxide_vm/src/vm_props.rs +++ b/crates/oxide_vm/src/vm_props.rs @@ -2057,4 +2057,29 @@ mod tests { let ns = unsafe { &*ns_ptr }; assert!(!oxide_builtins::module::deferred_state(ns).unwrap().evaluated, "symbol-like 键不应触发求值"); } + + #[test] + fn inline_bytecode_body_catch_captures_native_throw() { + let (mut vm, _fn_val) = deferred_vm_with_function(); + // 字节码函数:try 体内调 native(JSON.parse 抛 SyntaxError),catch 捕获原值。 + // 经 call_function_sync(inline 路径,native_call_depth > 0)调用,钉住 + // raise_call_error 深度 >0 物化 + 内联体内 try 处理器捕获路径。 + let f = vm + .create_dynamic_function(&[], "try { JSON.parse('not json'); } catch (e) { return e; }", false, false) + .expect("create function"); + let result = vm.call_function_sync(f, JsValue::undefined(), &[]).expect("f 应正常返回"); + // 捕获值应为原 SyntaxError 对象(物化后由体内 catch 捕获,非空/非 undefined)。 + assert!(result.is_object(), "捕获值应为错误对象"); + let err_ptr = result.as_js_object_ptr(); + assert!(!err_ptr.is_null()); + let err = unsafe { &*err_ptr }; + let name_si = vm.kernel_core.perm_interner().intern("name").0; + let name = vm.ordinary_get(err, name_si, result).expect("read name"); + let expected = JsValue::perm_string( + vm.kernel_core + .perm_interner() + .string_ptr(vm.kernel_core.perm_interner().intern("SyntaxError").0), + ); + assert_eq!(name, expected, "捕获值应为 SyntaxError"); + } }