mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-26 19:51:11 +08:00
feat: 星号导出歧义名从命名空间省略
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_runtime_api::{NativeResult, VmHost};
|
||||
use oxide_types::object::{Cell, JsObject, PropAttributes};
|
||||
use oxide_types::object::{Cell, JsObject, PropAttributes, PropMetaEntry};
|
||||
use oxide_types::private_key::make_well_known_symbol_key;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
@@ -34,12 +34,14 @@ fn type_error<H: VmHost>(vm: &mut H, msg: &str) -> NativeResult {
|
||||
|
||||
/// 命名空间条目表的来源身份(provenance)。
|
||||
///
|
||||
/// `Local` 为本模块直接声明或本地再导出的导出;`Reexport` 预留跨模块 star 再导出
|
||||
/// 的来源身份,由 star 导出任务填入,避免另立 per-namespace 表形成两套判据。
|
||||
/// `Local` 为本模块直接声明的导出;`Reexport` 描述跨模块再导出的 `(模块, 绑定)`
|
||||
/// 身份,`direct` 区分显式再导出(`export { x } from`)与 star 复制的间接导出;
|
||||
/// `Ambiguous` 是 star 冲突后的省略哨兵,该名不再出现在命名空间且一旦歧义恒歧义。
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ModuleNsOrigin {
|
||||
Local,
|
||||
Reexport { path_id: u32, binding_id: u32, direct: bool },
|
||||
Ambiguous,
|
||||
}
|
||||
|
||||
/// 单个导出名的活值状态。
|
||||
@@ -95,7 +97,7 @@ fn install_ns_table(obj: &mut JsObject) -> *mut ModuleNsTable {
|
||||
table
|
||||
}
|
||||
|
||||
/// 追加一条未初始化的 `Value` 条目。
|
||||
/// 追加一条未初始化的 `Value` 条目(来源为本地导出)。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 调用方保证 `name_si` 尚未入表(入口均为先查后插)。
|
||||
@@ -103,6 +105,7 @@ fn push_uninitialized_entry(table: *mut ModuleNsTable, name_si: u32) {
|
||||
push_entry(
|
||||
table,
|
||||
name_si,
|
||||
ModuleNsOrigin::Local,
|
||||
ModuleNsState::Value {
|
||||
value: JsValue::undefined(),
|
||||
initialized: false,
|
||||
@@ -110,24 +113,12 @@ fn push_uninitialized_entry(table: *mut ModuleNsTable, name_si: u32) {
|
||||
);
|
||||
}
|
||||
|
||||
/// 追加一条已初始化的 `Value` 条目。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 调用方保证 `name_si` 尚未入表。
|
||||
fn push_initialized_entry(table: *mut ModuleNsTable, name_si: u32, value: JsValue) {
|
||||
push_entry(table, name_si, ModuleNsState::Value { value, initialized: true });
|
||||
}
|
||||
|
||||
/// 向条目表追加一条条目。
|
||||
/// 向条目表追加一条带来源身份的条目。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - table 归调用方持有的 ns 对象所有,在同一 VM 世代内有效。
|
||||
fn push_entry(table: *mut ModuleNsTable, name_si: u32, state: ModuleNsState) {
|
||||
let entry = ModuleNsEntry {
|
||||
name_si,
|
||||
origin: ModuleNsOrigin::Local,
|
||||
state,
|
||||
};
|
||||
fn push_entry(table: *mut ModuleNsTable, name_si: u32, origin: ModuleNsOrigin, state: ModuleNsState) {
|
||||
let entry = ModuleNsEntry { name_si, origin, state };
|
||||
// SAFETY: table 归调用方持有的 ns 对象所有,在同一 VM 世代内有效。
|
||||
unsafe {
|
||||
(*table).entries.push(entry);
|
||||
@@ -152,6 +143,10 @@ pub fn module_ns_export(obj: &JsObject, key_si: u32) -> Option<ModuleNsQuery> {
|
||||
// SAFETY: table 指向本 ns 对象持有的 Box<ModuleNsTable>,随对象存活。
|
||||
let table_ref = unsafe { &*table };
|
||||
let entry = table_ref.entries.get(entry_index(table_ref, key_si)?)?;
|
||||
// 歧义哨兵条目:该名已从命名空间省略,查询一律视为非导出(落普通路径)。
|
||||
if matches!(entry.origin, ModuleNsOrigin::Ambiguous) {
|
||||
return None;
|
||||
}
|
||||
match &entry.state {
|
||||
ModuleNsState::Value { value, initialized } => {
|
||||
if *initialized {
|
||||
@@ -370,16 +365,109 @@ pub fn module_object<H: VmHost>(vm: &mut H, _args: &[u8]) -> NativeResult {
|
||||
NativeResult::Ok(JsValue::from_js_object(obj))
|
||||
}
|
||||
|
||||
/// `__moduleSet(ns, name, value)`:在命名空间上定义或更新导出属性。
|
||||
/// 写入命名空间导出:命中条目则更新活值、真实属性槽与来源身份;否则经瞬态可扩展
|
||||
/// 窗口定义真实属性槽,并在 ns 有条目表时追加条目。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. live ns 命中既有条目:`Cell` 条目 no-op(活值由 cell 承载);`Value` 条目
|
||||
/// 更新活值并同步真实属性槽,键集不变故跳过重排。
|
||||
/// 2. 其余(无表 / 非条目 / 新键):经瞬态可扩展窗口定义或改值真实属性槽;
|
||||
/// live ns 追加已初始化条目,最后按规范顺序重排导出键。
|
||||
/// # 边界与前提
|
||||
/// - `Cell` 条目 no-op:被捕获导出的活值由共享 cell 承载,直接写槽会与 cell 分裂。
|
||||
/// - 条目存在但真实槽缺失(歧义省略后显式导出重新定义):落新键定义路径补回槽。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 修改条目表状态与 ns 属性槽;新键会重排 `[[OwnPropertyKeys]]` 顺序。
|
||||
/// - 修改条目表状态/来源身份与 ns 属性槽;新键或删槽重定义时重排
|
||||
/// `[[OwnPropertyKeys]]` 顺序。
|
||||
fn set_export<H: VmHost>(
|
||||
vm: &mut H, obj: &mut JsObject, name_si: u32, value: JsValue, origin: ModuleNsOrigin,
|
||||
) -> Result<(), String> {
|
||||
let table = ns_table_ptr(obj);
|
||||
let existing = if table.is_null() {
|
||||
None
|
||||
} else {
|
||||
// SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。
|
||||
unsafe { entry_index(&*table, name_si) }
|
||||
};
|
||||
|
||||
// Cell 条目 no-op:被捕获导出的活值由共享 cell 承载,写槽会与 cell 分裂。
|
||||
if let Some(idx) = existing {
|
||||
// SAFETY: idx 来自刚完成的表内查找,仍在界内。
|
||||
if matches!(unsafe { &(&(*table).entries)[idx].state }, ModuleNsState::Cell(_)) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// 条目已存在且真实槽仍在:原地更新活值/来源,键集不变无需重排。
|
||||
if let Some(idx) = existing {
|
||||
if let Some(pos) = vm.get_own_property_slot(obj, name_si) {
|
||||
obj.set_prop_at(pos, value);
|
||||
// SAFETY: idx 来自刚完成的表内查找,仍在界内。
|
||||
unsafe {
|
||||
(&mut (*table).entries)[idx].origin = origin;
|
||||
(&mut (*table).entries)[idx].state = ModuleNsState::Value { value, initialized: true };
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// 歧义省略删槽后显式导出重新定义:落下方定义路径补回真实槽。
|
||||
}
|
||||
|
||||
// 导出定义窗口:命名空间创建起不可扩展,定义新导出须瞬态放开扩展性。窗口内
|
||||
// 不运行 JS,无可观察副作用;错误路径先复位以维持 non-extensible 不变式。
|
||||
obj.set_extensible(true);
|
||||
let result = vm.define_data_property(obj, name_si, value, ns_attrs());
|
||||
obj.set_extensible(false);
|
||||
result?;
|
||||
|
||||
if !table.is_null() {
|
||||
if let Some(idx) = existing {
|
||||
// SAFETY: idx 来自刚完成的表内查找;重定义后同步条目,勿重复入表。
|
||||
unsafe {
|
||||
(&mut (*table).entries)[idx].origin = origin;
|
||||
(&mut (*table).entries)[idx].state = ModuleNsState::Value { value, initialized: true };
|
||||
}
|
||||
} else {
|
||||
push_entry(table, name_si, origin, ModuleNsState::Value { value, initialized: true });
|
||||
}
|
||||
}
|
||||
// 每次键集变化后重排:自导入在 body 内即可见有序的 [[OwnPropertyKeys]]。
|
||||
crate::object::sort_namespace_exports(vm, obj);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 从命名空间属性表移除一个导出槽(歧义省略用),保留其余字符串与 Symbol 键及其
|
||||
/// 描述符。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 命名空间导出属性为 non-configurable,本函数不经 `[[Delete]]`,直接重建属性表
|
||||
/// 以同时保住 `@@toStringTag` Symbol 键。
|
||||
fn remove_export_slot<H: VmHost>(vm: &mut H, obj: &mut JsObject, name_si: u32) {
|
||||
let mut retained: Vec<(u32, JsValue, Option<PropMetaEntry>)> = crate::object::walk_own_keys(vm, obj)
|
||||
.into_iter()
|
||||
.filter(|(si, _)| *si != name_si)
|
||||
.map(|(si, pos)| (si, obj.get_prop_at(pos), obj.prop_meta_at(pos)))
|
||||
.collect();
|
||||
let symbols: Vec<(u32, JsValue, Option<PropMetaEntry>)> = crate::object::walk_own_symbol_keys(vm, obj)
|
||||
.into_iter()
|
||||
.map(|(si, pos)| (si, obj.get_prop_at(pos), obj.prop_meta_at(pos)))
|
||||
.collect();
|
||||
obj.set_shape_id(EMPTY_SHAPE_ID);
|
||||
obj.clear_props();
|
||||
for (si, value, meta) in retained.drain(..).chain(symbols) {
|
||||
let shape = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
|
||||
obj.set_shape_id(shape);
|
||||
let pos = obj.push_prop(value);
|
||||
if let Some(meta) = meta {
|
||||
if meta.is_accessor {
|
||||
obj.set_accessor_meta(pos, meta.get, meta.set, meta.attributes);
|
||||
} else {
|
||||
obj.set_data_meta(pos, meta.attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
obj.bump_generation();
|
||||
}
|
||||
|
||||
/// `__moduleSet(ns, name, value)`:在命名空间上定义或更新本地导出属性。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 同 `set_export`;本地导出来源身份恒为 `Local`,覆盖先前 star 条目时复位来源。
|
||||
pub fn module_set<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 4 {
|
||||
return type_error(vm, "__moduleSet: 3 arguments required");
|
||||
@@ -394,41 +482,56 @@ pub fn module_set<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let value = vm.reg(args[3]);
|
||||
let name_si = vm.property_key_si(name_val);
|
||||
let obj = unsafe { &mut *ns_ptr };
|
||||
|
||||
// live 条目路径:命中条目则更新活值并同步真实槽,键集不变无需重排。
|
||||
let table = ns_table_ptr(obj);
|
||||
if !table.is_null() {
|
||||
// SAFETY: table 归本 ns 对象持有,生命周期见 `module_ns_export`。
|
||||
if let Some(idx) = unsafe { entry_index(&*table, name_si) } {
|
||||
// SAFETY: idx 来自刚完成的表内查找,仍在界内。
|
||||
let entry_state = unsafe { &mut (&mut (*table).entries)[idx].state };
|
||||
match entry_state {
|
||||
ModuleNsState::Cell(_) => return NativeResult::Ok(JsValue::undefined()),
|
||||
ModuleNsState::Value { value: slot, initialized } => {
|
||||
*slot = value;
|
||||
*initialized = true;
|
||||
if let Some(pos) = vm.get_own_property_slot(obj, name_si) {
|
||||
obj.set_prop_at(pos, value);
|
||||
}
|
||||
return NativeResult::Ok(JsValue::undefined());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出定义窗口:命名空间创建起不可扩展,定义新导出须瞬态放开扩展性。窗口内
|
||||
// 不运行 JS,无可观察副作用;错误路径先复位以维持 non-extensible 不变式。
|
||||
obj.set_extensible(true);
|
||||
let result = vm.define_data_property(obj, name_si, value, ns_attrs());
|
||||
obj.set_extensible(false);
|
||||
if let Err(e) = result {
|
||||
if let Err(e) = set_export(vm, obj, name_si, value, ModuleNsOrigin::Local) {
|
||||
return NativeResult::Err(crate::error::create_error(vm, &e));
|
||||
}
|
||||
if !table.is_null() {
|
||||
push_initialized_entry(table, name_si, value);
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
/// `__moduleSetReexport(ns, name, value, dep_path, imported)`:注册显式再导出
|
||||
/// (`export { x } from` / `export * as ns from` / 导入绑定再导出),记录跨模块
|
||||
/// 来源身份 `(dep_path, imported)`,使同一绑定经不同路径转发时来源 token 相等。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 同 `module_set`;ns 无条目表时惰性安装,来源标为 `direct` 再导出。
|
||||
pub fn module_set_reexport<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 6 {
|
||||
return type_error(vm, "__moduleSetReexport: 5 arguments required");
|
||||
}
|
||||
let ns_val = vm.reg(args[1]);
|
||||
let ns_ptr = match vm.checked_object_ptr(ns_val, "__moduleSetReexport: target is not an object") {
|
||||
Ok(Some(p)) => p,
|
||||
Ok(None) => return type_error(vm, "__moduleSetReexport: 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 path_val = vm.reg(args[4]);
|
||||
let imported_val = vm.reg(args[5]);
|
||||
let name_si = vm.property_key_si(name_val);
|
||||
let Some(dep_path) = vm.lookup_str(path_val) else {
|
||||
return type_error(vm, "__moduleSetReexport: module path is not a string");
|
||||
};
|
||||
let Some(imported) = vm.lookup_str(imported_val) else {
|
||||
return type_error(vm, "__moduleSetReexport: imported name is not a string");
|
||||
};
|
||||
let obj = unsafe { &mut *ns_ptr };
|
||||
if !obj.is_module_namespace() {
|
||||
return type_error(vm, "__moduleSetReexport: target is not a module namespace");
|
||||
}
|
||||
if ns_table_ptr(obj).is_null() {
|
||||
install_ns_table(obj);
|
||||
}
|
||||
let path_id = vm.kernel_core().perm_interner().intern(&dep_path).0;
|
||||
let binding_id = vm.kernel_core().perm_interner().intern(&imported).0;
|
||||
let origin = ModuleNsOrigin::Reexport {
|
||||
path_id,
|
||||
binding_id,
|
||||
direct: true,
|
||||
};
|
||||
if let Err(e) = set_export(vm, obj, name_si, value, origin) {
|
||||
return NativeResult::Err(crate::error::create_error(vm, &e));
|
||||
}
|
||||
// 每次导出写入后重排:自导入在 body 内即可见有序的 [[OwnPropertyKeys]]。
|
||||
crate::object::sort_namespace_exports(vm, obj);
|
||||
NativeResult::Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -481,13 +584,55 @@ pub fn module_link_get<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
}
|
||||
|
||||
/// `__moduleStar(dst, src)`:把 src 命名空间的全部导出(除 default)复制到 dst。
|
||||
/// 取 src 命名空间某导出名的来源身份。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - src 条目表命中且来源为 `Reexport`:沿用其 `(模块, 绑定)`,`direct` 归一为
|
||||
/// false(经 star 复制到 dst 的是间接提供)。
|
||||
/// - 其余(无表 / `Local` / 条目缺失):回退 `(src_path_id, name_si)`。只声明本地
|
||||
/// 导出的源不建条目表,回退 token 使不同源的同名本地导出身份不同。
|
||||
fn star_source_origin(src: &JsObject, name_si: u32, src_path_id: u32) -> ModuleNsOrigin {
|
||||
let table = ns_table_ptr(src);
|
||||
if !table.is_null() {
|
||||
// SAFETY: table 归 src 持有,生命周期见 `module_ns_export`。
|
||||
if let Some(idx) = unsafe { entry_index(&*table, name_si) } {
|
||||
// SAFETY: idx 来自刚完成的表内查找,仍在界内。
|
||||
if let ModuleNsOrigin::Reexport { path_id, binding_id, .. } = unsafe { (&(*table).entries)[idx].origin } {
|
||||
return ModuleNsOrigin::Reexport {
|
||||
path_id,
|
||||
binding_id,
|
||||
direct: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
ModuleNsOrigin::Reexport {
|
||||
path_id: src_path_id,
|
||||
binding_id: name_si,
|
||||
direct: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// `__moduleStar(dst, src, src_path)`:把 src 命名空间的可枚举导出(除 default)
|
||||
/// 复制到 dst,并按来源身份解析同名冲突。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 每个 src 导出名取来源 `(模块, 绑定)`;`default` 跳过。
|
||||
/// 2. dst 条目为 `Ambiguous`:恒跳过(一旦歧义恒省略)。
|
||||
/// 3. dst 条目为显式导出(`Local` / direct `Reexport`):显式恒胜,跳过。
|
||||
/// 4. dst 条目为间接 `Reexport`:`(模块, 绑定)` 相同则保留;不同则删槽并记
|
||||
/// `Ambiguous`。
|
||||
/// 5. dst 无条目:真实槽已存在视为显式导出跳过;否则复制槽并记间接来源。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 在 dst 上定义/删除真实属性槽,惰性安装条目表,并按规范顺序重排导出键。
|
||||
pub fn module_star<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 3 {
|
||||
return type_error(vm, "__moduleStar: 2 arguments required");
|
||||
if args.len() < 4 {
|
||||
return type_error(vm, "__moduleStar: 3 arguments required");
|
||||
}
|
||||
let dst_val = vm.reg(args[1]);
|
||||
let src_val = vm.reg(args[2]);
|
||||
let src_path_val = vm.reg(args[3]);
|
||||
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"),
|
||||
@@ -498,32 +643,73 @@ pub fn module_star<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Ok(None) => return type_error(vm, "__moduleStar: source is not an object"),
|
||||
Err(e) => return NativeResult::Err(crate::error::create_error(vm, &e)),
|
||||
};
|
||||
let Some(src_path) = vm.lookup_str(src_path_val) else {
|
||||
return type_error(vm, "__moduleStar: source path is not a string");
|
||||
};
|
||||
let src_path_id = vm.kernel_core().perm_interner().intern(&src_path).0;
|
||||
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 };
|
||||
// live dst:star 复制的新导出名须一并入表,否则 2b 的活值读/枚举看不到条目。
|
||||
let dst_table = ns_table_ptr(dst);
|
||||
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);
|
||||
// star 导出同 module_set:定义新导出走瞬态可扩展窗口,错误路径先复位。
|
||||
dst.set_extensible(true);
|
||||
let result = vm.define_data_property(dst, name_si, value, ns_attrs());
|
||||
dst.set_extensible(false);
|
||||
if let Err(e) = result {
|
||||
return NativeResult::Err(crate::error::create_error(vm, &e));
|
||||
}
|
||||
if !dst_table.is_null() {
|
||||
// 2a 保持快照语义:star 名立即视为已初始化。
|
||||
push_initialized_entry(dst_table, name_si, value);
|
||||
let src_origin = star_source_origin(src, name_si, src_path_id);
|
||||
// live dst:star 复制的新导出名须一并入表,否则活值读/枚举看不到条目。
|
||||
let dst_table = ns_table_ptr(dst);
|
||||
let dst_idx = if dst_table.is_null() {
|
||||
None
|
||||
} else {
|
||||
unsafe { entry_index(&*dst_table, name_si) }
|
||||
};
|
||||
match dst_idx {
|
||||
Some(idx) => {
|
||||
// SAFETY: idx 来自刚完成的表内查找,仍在界内。
|
||||
let dst_origin = unsafe { (&(*dst_table).entries)[idx].origin };
|
||||
match dst_origin {
|
||||
ModuleNsOrigin::Ambiguous => continue,
|
||||
ModuleNsOrigin::Local | ModuleNsOrigin::Reexport { direct: true, .. } => continue,
|
||||
ModuleNsOrigin::Reexport { path_id, binding_id, .. } => {
|
||||
let ModuleNsOrigin::Reexport {
|
||||
path_id: sp, binding_id: sb, ..
|
||||
} = src_origin
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
if path_id == sp && binding_id == sb {
|
||||
continue;
|
||||
}
|
||||
// 不同绑定同名:删除导出槽并记歧义哨兵,清空可回收的活值边。
|
||||
remove_export_slot(vm, dst, name_si);
|
||||
// SAFETY: idx 仍指向本表条目,删除槽不改动条目表。
|
||||
unsafe {
|
||||
(&mut (*dst_table).entries)[idx].origin = ModuleNsOrigin::Ambiguous;
|
||||
(&mut (*dst_table).entries)[idx].state = ModuleNsState::Value {
|
||||
value: JsValue::undefined(),
|
||||
initialized: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// 真实槽已存在但无条目:视为先前显式导出(本地导出不建表),显式恒胜。
|
||||
if vm.get_own_property_slot(dst, name_si).is_some() {
|
||||
continue;
|
||||
}
|
||||
let value = src.get_prop_at(pos);
|
||||
let table = if dst_table.is_null() { install_ns_table(dst) } else { dst_table };
|
||||
// star 导出同 module_set:定义新导出走瞬态可扩展窗口,错误路径先复位。
|
||||
dst.set_extensible(true);
|
||||
let result = vm.define_data_property(dst, name_si, value, ns_attrs());
|
||||
dst.set_extensible(false);
|
||||
if let Err(e) = result {
|
||||
return NativeResult::Err(crate::error::create_error(vm, &e));
|
||||
}
|
||||
// 快照语义:star 名立即视为已初始化。
|
||||
push_entry(table, name_si, src_origin, ModuleNsState::Value { value, initialized: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
// star 复制后重排:直接导出与 star 导出的合并键序保持规范顺序。
|
||||
|
||||
@@ -110,7 +110,7 @@ pub fn key_si_to_js_value<H: VmHost>(vm: &mut H, si: u32) -> JsValue {
|
||||
/// 与 [`walk_own_keys`] 互补:只返回 Symbol 键。返回 `(属性键 si, 绝对存储索引)`:
|
||||
/// 槽位计数须含全部非空节点(含字符串键)并对数组加元素区偏移,与物理存储及
|
||||
/// [`walk_own_keys`] 的口径一致,消费方(delete 重建)才能按槽位取回正确值。
|
||||
fn walk_own_symbol_keys<H: VmHost>(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> {
|
||||
pub(crate) fn walk_own_symbol_keys<H: VmHost>(vm: &H, obj: &JsObject) -> Vec<(u32, u32)> {
|
||||
let mut keys: Vec<(u32, u32)> = Vec::new();
|
||||
let shape_id = obj.shape_id();
|
||||
let mut shape_ids = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user