From b650ecb3cb9df5d752c3e3af29b05eb385ca4d94 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 12 Aug 2026 17:37:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20module=20natives=EF=BC=88=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4=E5=AF=B9=E8=B1=A1/=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E8=AF=BB=E5=86=99/star=20=E5=90=88=E5=B9=B6/seal/eval?= =?UTF-8?q?/data=20+=20=E9=93=BE=E6=8E=A5=E6=A3=80=E6=9F=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - __moduleObject:null 原型命名空间,带 @@toStringTag=Module(w/e/c=false) - __moduleSet:定义不可写/可枚举/不可配置导出属性 - __moduleGet:模块命名空间 exotic Get(缺失导出抛 TypeError) - __moduleLinkGet:import 绑定链接期检查(缺失导出抛 SyntaxError) - __moduleStar:export * 合并,冲突名跳过(dst 已有属性省略) - __moduleSeal:封冻命名空间 - __moduleEval:同步执行依赖模块并返回命名空间 - __moduleData:json/text 数据模块命名空间 --- crates/oxide_builtins/src/lib.rs | 2 + crates/oxide_builtins/src/module.rs | 220 ++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 crates/oxide_builtins/src/module.rs diff --git a/crates/oxide_builtins/src/lib.rs b/crates/oxide_builtins/src/lib.rs index 107fc90c..63c120e8 100644 --- a/crates/oxide_builtins/src/lib.rs +++ b/crates/oxide_builtins/src/lib.rs @@ -35,6 +35,8 @@ pub mod json; pub mod map; /// Math 内置对象实现(数学函数)。 pub mod math; +/// 模块命名空间与求值辅助(import 实现内部用)。 +pub mod module; /// Number 内置对象实现(constructor 与 toFixed/isInteger 等)。 pub mod number; /// Object 内置对象实现(keys/create/assign/defineProperty 等静态方法)。 diff --git a/crates/oxide_builtins/src/module.rs b/crates/oxide_builtins/src/module.rs new file mode 100644 index 00000000..1007cf25 --- /dev/null +++ b/crates/oxide_builtins/src/module.rs @@ -0,0 +1,220 @@ +//! 模块命名空间与求值辅助(import 实现内部用,非 JS 可见标准 API)。 +//! +//! 命名约定 `__module*`:由编译器模块 prelude 发出(`lookup_or_builtin` 解析为全局槽, +//! VM 在 frame push 时从 global 对象取回 native 函数)。当前为快照式链接: +//! 依赖模块先整体求值并返回命名空间对象,导入方从中读取导出值; +//! live binding / source-phase / defer 语义留待后续轮次。 + +use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; +use oxide_runtime_api::{NativeResult, VmHost}; +use oxide_types::object::{JsObject, PropAttributes}; +use oxide_types::value::JsValue; + +/// 模块命名空间导出属性描述符:不可写、可枚举、不可配置(module namespace exotic)。 +fn ns_attrs() -> PropAttributes { + PropAttributes::new(false, true, false) +} + +fn type_error(vm: &mut H, msg: &str) -> NativeResult { + NativeResult::Err(crate::error::create_type_error(vm, msg)) +} + +/// `__moduleObject()`:创建模块命名空间对象(null 原型,带 @@toStringTag)。 +pub fn module_object(vm: &mut H, _args: &[u8]) -> NativeResult { + let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + let tag_si = vm.kernel_core().perm_interner().intern("@@toStringTag").0; + let tag_val = vm.new_string("Module"); + let obj_ref = unsafe { &mut *obj }; + if let Err(e) = vm.define_data_property( + obj_ref, + tag_si, + tag_val, + PropAttributes::new(false, false, false), + ) { + return NativeResult::Err(crate::error::create_error(vm, &e)); + } + NativeResult::Ok(JsValue::from_js_object(obj)) +} + +/// `__moduleSet(ns, name, value)`:在命名空间上定义导出属性。 +pub fn module_set(vm: &mut H, args: &[u8]) -> NativeResult { + if args.len() < 4 { + return type_error(vm, "__moduleSet: 3 arguments required"); + } + let ns_val = vm.reg(args[1]); + let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSet: target is not an object") { + Ok(Some(p)) => p, + Ok(None) => return type_error(vm, "__moduleSet: target is not an object"), + Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), + }; + let name_val = vm.reg(args[2]); + let value = vm.reg(args[3]); + let name_si = vm.property_key_si(name_val); + let obj = unsafe { &mut *ns_ptr }; + if let Err(e) = vm.define_data_property(obj, name_si, value, ns_attrs()) { + return NativeResult::Err(crate::error::create_error(vm, &e)); + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `__moduleGet(ns, name)`:读取命名空间导出属性。 +pub fn module_get(vm: &mut H, args: &[u8]) -> NativeResult { + if args.len() < 3 { + return type_error(vm, "__moduleGet: 2 arguments required"); + } + let ns_val = vm.reg(args[1]); + let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleGet: target is not an object") { + Ok(Some(p)) => p, + Ok(None) => return type_error(vm, "__moduleGet: target is not an object"), + Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), + }; + let name_val = vm.reg(args[2]); + let name_si = vm.property_key_si(name_val); + let obj = unsafe { &*ns_ptr }; + // 模块命名空间 exotic Get:未导出属性抛 TypeError(而非 undefined)。 + if vm.get_own_property_slot(obj, name_si).is_none() { + return type_error(vm, "__moduleGet: requested export is not exported"); + } + match vm.ordinary_get(obj, name_si, ns_val) { + Ok(v) => NativeResult::Ok(v), + Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), + } +} + +/// `__moduleLinkGet(ns, name)`:import 绑定初始化(链接期语义)。 +/// 与 `__moduleGet` 的区别:缺失导出抛 SyntaxError(模块声明实例化期的 +/// 绑定解析错误),而非命名空间访问的 TypeError。 +pub fn module_link_get(vm: &mut H, args: &[u8]) -> NativeResult { + if args.len() < 3 { + return type_error(vm, "__moduleLinkGet: 2 arguments required"); + } + let ns_val = vm.reg(args[1]); + let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleLinkGet: target is not an object") { + Ok(Some(p)) => p, + Ok(None) => return type_error(vm, "__moduleLinkGet: target is not an object"), + Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), + }; + let name_val = vm.reg(args[2]); + let name_si = vm.property_key_si(name_val); + let obj = unsafe { &*ns_ptr }; + if vm.get_own_property_slot(obj, name_si).is_none() { + return NativeResult::Err(crate::error::create_syntax_error( + vm, + "requested module export is not exported", + )); + } + match vm.ordinary_get(obj, name_si, ns_val) { + Ok(v) => NativeResult::Ok(v), + Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), + } +} + +/// `__moduleStar(dst, src)`:把 src 命名空间的全部导出(除 default)复制到 dst。 +pub fn module_star(vm: &mut H, args: &[u8]) -> NativeResult { + if args.len() < 3 { + return type_error(vm, "__moduleStar: 2 arguments required"); + } + let dst_val = vm.reg(args[1]); + let src_val = vm.reg(args[2]); + let dst_ptr = match vm.checked_object_ptr(dst_val, "__moduleStar: target is not an object") { + Ok(Some(p)) => p, + Ok(None) => return type_error(vm, "__moduleStar: target is not an object"), + Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), + }; + let src_ptr = match vm.checked_object_ptr(src_val, "__moduleStar: source is not an object") { + Ok(Some(p)) => p, + Ok(None) => return type_error(vm, "__moduleStar: source is not an object"), + Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), + }; + let src = unsafe { &*src_ptr }; + let default_si = vm.kernel_core().perm_interner().intern("default").0; + let keys = crate::object::walk_own_keys(vm, src); + let dst = unsafe { &mut *dst_ptr }; + for (name_si, pos) in keys { + if name_si == default_si { + continue; + } + // export * 冲突省略:名称已被本命名空间直接导出或先前 star 占用时跳过 + // (规范 StarExportEntries 求值后,ambiguous 名称从命名空间省略)。 + if vm.get_own_property_slot(dst, name_si).is_some() { + continue; + } + let value = src.get_prop_at(pos); + if let Err(e) = vm.define_data_property(dst, name_si, value, ns_attrs()) { + return NativeResult::Err(crate::error::create_error(vm, &e)); + } + } + NativeResult::Ok(JsValue::undefined()) +} + +/// `__moduleSeal(ns)`:封冻命名空间(不可扩展;toStringTag 留待后续)。 +pub fn module_seal(vm: &mut H, args: &[u8]) -> NativeResult { + if args.len() < 2 { + return type_error(vm, "__moduleSeal: 1 argument required"); + } + let ns_val = vm.reg(args[1]); + let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSeal: target is not an object") { + Ok(Some(p)) => p, + Ok(None) => return type_error(vm, "__moduleSeal: target is not an object"), + Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)), + }; + let obj = unsafe { &mut *ns_ptr }; + obj.set_extensible(false); + NativeResult::Ok(ns_val) +} + +/// `__moduleEval(fn)`:同步执行依赖模块(fn 为编译期 CREATE_CLOSURE 的函数对象),返回其命名空间。 +pub fn module_eval(vm: &mut H, args: &[u8]) -> NativeResult { + if args.len() < 2 { + return type_error(vm, "__moduleEval: 1 argument required"); + } + let fn_val = vm.reg(args[1]); + if !fn_val.is_object() { + return type_error(vm, "__moduleEval: target is not a function"); + } + let ptr = fn_val.as_js_object_ptr(); + if ptr.is_null() || !unsafe { (*ptr).is_function() } { + return type_error(vm, "__moduleEval: target is not a function"); + } + match vm.call_function_sync(fn_val, JsValue::undefined(), &[]) { + Ok(v) => NativeResult::Ok(v), + Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)), + } +} + +/// `__moduleData(kind, content)`:构造数据模块命名空间(json/text;bytes 未支持)。 +pub fn module_data(vm: &mut H, args: &[u8]) -> NativeResult { + if args.len() < 3 { + return type_error(vm, "__moduleData: 2 arguments required"); + } + let kind_val = vm.reg(args[1]); + let content_val = vm.reg(args[2]); + if !kind_val.is_string() || !content_val.is_string() { + return type_error(vm, "__moduleData: kind/content must be strings"); + } + let kind = unsafe { oxide_runtime_api::string_data(kind_val) }.to_string(); + let default_val = match kind.as_str() { + "json" => { + // 复用 JSON.parse 的 serde 管线:args[1] 即内容字符串。 + let parse_result = crate::json::json_parse(vm, &[args[0], args[2]]); + match parse_result { + NativeResult::Ok(v) => v, + NativeResult::Err(e) => return NativeResult::Err(e), + NativeResult::TailCall { .. } => return type_error(vm, "__moduleData: unexpected tail call"), + } + } + "text" => { + let text = unsafe { oxide_runtime_api::string_data(content_val) }; + vm.new_string(text) + } + _ => return type_error(vm, "__moduleData: unsupported data kind"), + }; + let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); + let default_si = vm.kernel_core().perm_interner().intern("default").0; + let obj_ref = unsafe { &mut *obj }; + if let Err(e) = vm.define_data_property(obj_ref, default_si, default_val, ns_attrs()) { + return NativeResult::Err(crate::error::create_error(vm, &e)); + } + obj_ref.set_extensible(false); + NativeResult::Ok(JsValue::from_js_object(obj)) +}