mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 对象层惰性模块命名空间求值触发
This commit is contained in:
Generated
+19
-19
@@ -1160,7 +1160,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_builtins"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fast-float",
|
||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_bytecode"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxide_log",
|
||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cfg"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_emit",
|
||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_cli"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"bumpalo",
|
||||
@@ -1226,7 +1226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_code_cache"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"lru",
|
||||
"oxide_bytecode",
|
||||
@@ -1235,7 +1235,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_compiler"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1255,7 +1255,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_dce"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1269,7 +1269,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_emit"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"oxc_allocator",
|
||||
@@ -1284,7 +1284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_engine"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1296,7 +1296,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_ir"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_log",
|
||||
@@ -1305,7 +1305,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_kernel"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1319,7 +1319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1331,7 +1331,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
@@ -1340,7 +1340,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_parser"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
@@ -1355,7 +1355,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_regalloc"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
@@ -1371,7 +1371,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_runtime_api"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
@@ -1384,7 +1384,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_test262"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_compiler",
|
||||
@@ -1402,7 +1402,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_types"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"dashmap",
|
||||
@@ -1415,7 +1415,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "oxide_vm"
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"chrono",
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.105"
|
||||
version = "0.8.106"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -187,10 +187,12 @@ pub(crate) fn unit_string_value<H: VmHost>(vm: &mut H, unit: u16) -> JsValue {
|
||||
|
||||
/// 索引位置存在性判定(规范 HasProperty:自身与原型链任一层命中即存在,
|
||||
/// 数组元素区 hole 视同缺失,TypedArray 整数索引按视图长度判在界)。
|
||||
/// 数组/类数组目标非 deferred namespace,触发恒 no-op。
|
||||
pub(crate) fn arraylike_index_present<H: VmHost>(vm: &mut H, ptr: *mut JsObject, i: usize) -> bool {
|
||||
// SAFETY: ptr 来自 this/参数对象,指向存活对象。
|
||||
let key_si = vm.string_key_si(&i.to_string());
|
||||
vm.has_property(unsafe { &*ptr }, key_si)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// 元素读取失败时直接把异常作为 NativeResult::Err 返回。
|
||||
|
||||
@@ -26,7 +26,8 @@ fn gated_get<H: VmHost>(
|
||||
vm: &mut H, arr_ptr: *mut JsObject, index: usize, recv: JsValue,
|
||||
) -> Result<Option<JsValue>, JsValue> {
|
||||
let key_si = vm.string_key_si(&index.to_string());
|
||||
if !vm.has_property(unsafe { &*arr_ptr }, key_si) {
|
||||
// 数组目标非 deferred namespace,触发恒 no-op;错误按缺失处理(不可达)。
|
||||
if !vm.has_property(unsafe { &*arr_ptr }, key_si).unwrap_or(false) {
|
||||
return Ok(None);
|
||||
}
|
||||
let val = match vm.ordinary_get(unsafe { &*arr_ptr }, key_si, recv) {
|
||||
@@ -40,7 +41,9 @@ fn gated_get<H: VmHost>(
|
||||
/// 成功。数组元素区与命名属性双路径交由 delete_own_property_outcome 判定。
|
||||
fn delete_prop_or_throw<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> Result<(), JsValue> {
|
||||
match crate::object::delete_own_property_outcome(vm, obj, key_si) {
|
||||
crate::object::DeleteOutcome::NonConfigurable => Err(array_type_error(vm, "Cannot delete property")),
|
||||
// deferred namespace 求值触发失败(cyclic / 失败缓存重抛)原值上抛。
|
||||
Err(exc) => Err(exc),
|
||||
Ok(crate::object::DeleteOutcome::NonConfigurable) => Err(array_type_error(vm, "Cannot delete property")),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -769,7 +772,8 @@ pub fn array_reverse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let lower_si = vm.string_key_si(&lower.to_string());
|
||||
let upper_si = vm.string_key_si(&upper.to_string());
|
||||
// 先下后上读值(getter 副作用序对齐规范);hole 与原型缺失判端不存在。
|
||||
let lower_val = if vm.has_property(unsafe { &*arr_ptr }, lower_si) {
|
||||
// 数组目标非 deferred namespace,触发恒 no-op;错误按缺失处理(不可达)。
|
||||
let lower_val = if vm.has_property(unsafe { &*arr_ptr }, lower_si).unwrap_or(false) {
|
||||
match vm.ordinary_get(unsafe { &*arr_ptr }, lower_si, recv) {
|
||||
Ok(v) => Some(v),
|
||||
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
||||
@@ -782,7 +786,7 @@ pub fn array_reverse<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if let Some(v) = lower_val {
|
||||
vm.set_reg(0, v);
|
||||
}
|
||||
let upper_val = if vm.has_property(unsafe { &*arr_ptr }, upper_si) {
|
||||
let upper_val = if vm.has_property(unsafe { &*arr_ptr }, upper_si).unwrap_or(false) {
|
||||
match vm.ordinary_get(unsafe { &*arr_ptr }, upper_si, recv) {
|
||||
Ok(v) => Some(v),
|
||||
Err(msg) => return NativeResult::Err(from_engine_error(vm, &msg)),
|
||||
|
||||
@@ -627,6 +627,8 @@ fn same_value_number(a: f64, b: f64) -> bool {
|
||||
/// 均静默不抛(规范明注)。
|
||||
fn apply_child_result<H: VmHost>(vm: &mut H, child_ptr: *mut JsObject, child_si: u32, new_val: JsValue) {
|
||||
if new_val.is_undefined() {
|
||||
// 子节点为 JSON.parse 新建的 plain 对象(非 deferred namespace),触发
|
||||
// 恒 no-op;删除失败静默不抛(规范明注)。
|
||||
let _ = crate::object::delete_own_property(vm, unsafe { &mut *child_ptr }, child_si);
|
||||
} else {
|
||||
let _ = vm.define_data_property(
|
||||
|
||||
@@ -70,11 +70,14 @@ pub struct DeferredNsState {
|
||||
/// undefined(哨兵:自导入/祖先引用,模块函数在导入方 prelude 期不可得)。
|
||||
pub module: JsValue,
|
||||
/// `[[Evaluated]]`:依赖模块是否已求值(mixed 依赖构造时即为真;
|
||||
/// 首次触发成功后置真,不回退)。
|
||||
/// 求值完成(成功或失败)后置真,不回退)。
|
||||
pub evaluated: bool,
|
||||
/// `[[Namespace]]`:求值后返回的命名空间对象(mixed 依赖构造时即为真实 ns;
|
||||
/// 求值前为 undefined)。
|
||||
pub namespace: JsValue,
|
||||
/// 求值失败时缓存的原始错误值(再读再抛同一错误对象,保身份);
|
||||
/// 成功求值与求值前恒 `None`。
|
||||
pub error: Option<JsValue>,
|
||||
}
|
||||
|
||||
/// 模块命名空间条目表:挂在 ns 对象 `native_data` 上,是 exotic [[Get]] 的权威
|
||||
@@ -125,6 +128,52 @@ pub fn deferred_state(obj: &JsObject) -> Option<&DeferredNsState> {
|
||||
table_ref.deferred.as_ref()
|
||||
}
|
||||
|
||||
/// 可变访问 deferred namespace 状态盒(求值触发点写回 `[[Evaluated]]` /
|
||||
/// `[[Namespace]]` / 失败错误缓存)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 返回 `None`:非 module namespace / 无条目表 / 非 deferred 对象(`deferred` 为 `None`)。
|
||||
/// - 返回 `Some`:deferred 对象且已安装状态盒,供求值触发点读写内部槽。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 经 `native_data` 裸指针取 `&mut`(Box 堆分配、GC 不搬移,指针跨调用稳定),
|
||||
/// 不借 `&mut JsObject` 本体。
|
||||
#[expect(clippy::mut_from_ref)]
|
||||
pub fn deferred_state_mut(obj: &JsObject) -> Option<&mut DeferredNsState> {
|
||||
let table = ns_table_ptr(obj);
|
||||
if table.is_null() {
|
||||
return None;
|
||||
}
|
||||
// SAFETY: table 归本 ns 对象持有,生命周期见 `deferred_state`。
|
||||
let table_ref = unsafe { &mut *table };
|
||||
table_ref.deferred.as_mut()
|
||||
}
|
||||
|
||||
/// deferred namespace 读路径的权威命名空间对象:已求值且 `[[Namespace]]` 为
|
||||
/// 对象时返回真实 ns(活值与真实槽位都在真实 ns 的条目表,deferred 对象自身
|
||||
/// 槽位是逐名预注册占位);其余形态(非 deferred / 未求值 / 哨兵)返回对象自身。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - 非 deferred 对象恒返回自身,调用方无需先判 deferred 标志。
|
||||
/// - 求值成功后 `[[Namespace]]` 为 `__moduleEval` 返回的真实 ns 对象;求值失败
|
||||
/// 或哨兵(自导入/祖先)形态下无真实 ns,读路径回落对象自身。
|
||||
pub fn deferred_ns_read_target(obj: &JsObject) -> &JsObject {
|
||||
if let Some(state) = deferred_state(obj) {
|
||||
if state.evaluated {
|
||||
let ns = state.namespace;
|
||||
if ns.is_object() {
|
||||
let ptr = ns.as_js_object_ptr();
|
||||
if !ptr.is_null() {
|
||||
// SAFETY: ns 由 [[Namespace]] 槽持有(GC 边已登记),
|
||||
// 与 deferred 对象同代存活。
|
||||
return unsafe { &*ptr };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
obj
|
||||
}
|
||||
|
||||
/// 在条目表中按 interned 键定位条目下标。
|
||||
fn entry_index(table: &ModuleNsTable, key_si: u32) -> Option<usize> {
|
||||
table.entries.iter().position(|e| e.name_si == key_si)
|
||||
@@ -232,9 +281,10 @@ pub fn module_ns_export(obj: &JsObject, key_si: u32) -> Option<ModuleNsQuery> {
|
||||
|
||||
/// 收集条目表持有的 JsValue 边(已初始化 `Value` 与全部 `Cell` 内值),供 GC mark。
|
||||
///
|
||||
/// deferred namespace 对象额外持有 `[[Module]]` 与 `[[Namespace]]` 两条边:
|
||||
/// 依赖模块函数与求值后命名空间对象须经本单一注册点入 mark,漏登则函数在
|
||||
/// deferred ns 存活期间被回收,触发点解引用悬垂。
|
||||
/// deferred namespace 对象额外持有 `[[Module]]`、`[[Namespace]]` 与失败缓存
|
||||
/// `error`(Some 时)三条边:依赖模块函数、求值后命名空间对象与失败错误对象
|
||||
/// 须经本单一注册点入 mark,漏登则对应值在 deferred ns 存活期间被回收,
|
||||
/// 触发点解引用悬垂。
|
||||
pub fn module_ns_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
let table = ns_table_ptr(obj);
|
||||
if table.is_null() {
|
||||
@@ -261,6 +311,10 @@ pub fn module_ns_native_edges(obj: &JsObject) -> Vec<JsValue> {
|
||||
if let Some(deferred) = &table_ref.deferred {
|
||||
edges.push(deferred.module);
|
||||
edges.push(deferred.namespace);
|
||||
// 失败缓存的错误对象是 GC 边:漏登则 deferred ns 存活期被回收,再读解引用悬垂。
|
||||
if let Some(err) = &deferred.error {
|
||||
edges.push(*err);
|
||||
}
|
||||
}
|
||||
edges
|
||||
}
|
||||
@@ -844,6 +898,10 @@ pub fn module_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
|
||||
/// `__moduleEval(fn)`:同步执行依赖模块(fn 为编译期 CREATE_CLOSURE 的函数对象),返回其命名空间。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 调用前置「正在求值模块」标记为 `Some(fn)`、返回后还原(save/restore,嵌套
|
||||
/// `__moduleEval` 安全):deferred namespace 求值触发的 cyclic 守卫据此判自求值。
|
||||
pub fn module_eval<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
return type_error(vm, "__moduleEval: 1 argument required");
|
||||
@@ -856,10 +914,15 @@ pub fn module_eval<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
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(), &[]) {
|
||||
// 置位标记,调用返回后无条件还原(异常展开亦不残留)。
|
||||
let saved = vm.evaluating_module();
|
||||
vm.set_evaluating_module(Some(fn_val));
|
||||
let result = match vm.call_function_sync(fn_val, JsValue::undefined(), &[]) {
|
||||
Ok(v) => NativeResult::Ok(v),
|
||||
Err(e) => NativeResult::Err(crate::error::create_error(vm, &e)),
|
||||
}
|
||||
};
|
||||
vm.set_evaluating_module(saved);
|
||||
result
|
||||
}
|
||||
|
||||
/// `__moduleDeferObject(fn, real_ns)`:创建 deferred namespace 对象(`import defer * as` 产物)。
|
||||
@@ -871,7 +934,7 @@ pub fn module_eval<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
/// 2. 建 null 原型对象,定义 `@@toStringTag` = "Deferred Module"(不可写不可枚举不可配置)。
|
||||
/// 3. 置 module namespace + deferred 双标志,置 non-extensible。
|
||||
/// 4. 安装条目表并写状态盒:`[[Module]]` = fn、`[[Evaluated]]` = `real_ns` 为对象、
|
||||
/// `[[Namespace]]` = `real_ns`(或 undefined)。
|
||||
/// `[[Namespace]]` = `real_ns`(或 undefined)、失败错误缓存恒 `None`。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 新对象创建起即标记 module namespace exotic 并置 non-extensible(同 `__moduleObject`);
|
||||
@@ -912,6 +975,7 @@ pub fn module_defer_object<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
module: fn_val,
|
||||
evaluated: ns_val.is_object(),
|
||||
namespace: ns_val,
|
||||
error: None,
|
||||
});
|
||||
}
|
||||
NativeResult::Ok(JsValue::from_js_object(obj))
|
||||
@@ -1069,4 +1133,46 @@ mod tests {
|
||||
let state = deferred_state(ns_obj).expect("deferred state 应存活");
|
||||
assert_eq!(state.module.as_js_object_ptr(), fn_ptr, "[[Module]] 指针应稳定");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn module_defer_ns_gc_edge_keeps_error_alive() {
|
||||
let (mut vm, _fn_val) = vm_with_function();
|
||||
// 失败缓存的错误对象(模拟求值失败后缓存的原值)。
|
||||
let err_val = crate::error::create_type_error(&mut vm, "cached failure");
|
||||
let err_ptr = err_val.as_js_object_ptr();
|
||||
|
||||
// 创建 deferred ns(哨兵形态)。
|
||||
vm.set_reg(1, JsValue::undefined());
|
||||
vm.set_reg(2, JsValue::undefined());
|
||||
let ns_val = module_defer_object(&mut vm, &[0, 1, 2]).unwrap();
|
||||
|
||||
// 直接写入 error 字段(模拟失败缓存),置已求值。
|
||||
{
|
||||
let ns_obj = unsafe { &*ns_val.as_js_object_ptr() };
|
||||
let state = deferred_state_mut(ns_obj).expect("deferred state 应已安装");
|
||||
state.error = Some(err_val);
|
||||
state.evaluated = true;
|
||||
}
|
||||
|
||||
// 仅 ns 为根:清掉持有 err 与 fn 的寄存器,err 唯一可达路径是 ns 的 error 边。
|
||||
vm.set_reg(0, ns_val);
|
||||
vm.set_reg(1, JsValue::undefined());
|
||||
vm.set_reg(2, JsValue::undefined());
|
||||
|
||||
// 首收集达稳态(fn 无根被回收),记录稳态计数。
|
||||
vm.collect_session_gc();
|
||||
let steady_count = vm.session_object_count();
|
||||
|
||||
// 再收集一次:若 error 边漏登,err 被回收、计数回落。
|
||||
vm.collect_session_gc();
|
||||
assert_eq!(
|
||||
vm.session_object_count(),
|
||||
steady_count,
|
||||
"失败缓存错误应跨收集存活(GC 边已登记)"
|
||||
);
|
||||
// 原地 sweep 不搬移:ns 与状态盒原地保留,error 指针稳定。
|
||||
let ns_obj = unsafe { &*ns_val.as_js_object_ptr() };
|
||||
let state = deferred_state(ns_obj).expect("deferred state 应存活");
|
||||
assert_eq!(state.error.unwrap().as_js_object_ptr(), err_ptr, "error 指针应稳定");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,6 +259,12 @@ pub fn object_get_own_property_symbols<H: VmHost>(vm: &mut H, args: &[u8]) -> Na
|
||||
Err(err) => return NativeResult::Err(err),
|
||||
};
|
||||
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
|
||||
let symbols: Vec<JsValue> = {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
own_symbol_key_values(vm, obj)
|
||||
@@ -332,17 +338,28 @@ pub enum DeleteOutcome {
|
||||
/// # 注意事项
|
||||
/// - 数组元素存在性以 `prop_meta_at` 的 hole 标记判定,删除后重新写入元素
|
||||
/// 会自动清除 hole 标记恢复存在
|
||||
pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> DeleteOutcome {
|
||||
pub fn delete_own_property_outcome<H: VmHost>(
|
||||
vm: &mut H, obj: &mut JsObject, key_si: u32,
|
||||
) -> Result<DeleteOutcome, JsValue> {
|
||||
// deferred namespace 求值触发([[Delete]],symbol-like 键内部 no-op):
|
||||
// 触发失败(cyclic / 失败缓存重抛)以异常值上抛,由调用边界恢复。
|
||||
// uncaught 侧通道已还原为原错误值,原值重抛保身份。
|
||||
if let Err(msg) = vm.ensure_deferred_ns_evaluation(obj, Some(key_si)) {
|
||||
let exc = vm
|
||||
.take_uncaught_value()
|
||||
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
|
||||
return Err(exc);
|
||||
}
|
||||
// TA 数值键:界内索引不可配置(删除失败,元素零修改、镜像槽不同步);
|
||||
// 数字无效键视为缺失(删除成功,镜像槽同步 undefined,对非全局内置 TA
|
||||
// 为 no-op);非数字串 / symbol 键落下方普通属性路径。live 长 0
|
||||
// (detach / 收缩越界)时全数值键自然归数字无效,与读 / 写 / 定义面同口径。
|
||||
if obj.is_typed_array_obj() {
|
||||
match crate::typed_array::ta_index_gate(vm, obj, key_si) {
|
||||
crate::typed_array::TaIndexGate::NumericValid(_) => return DeleteOutcome::NonConfigurable,
|
||||
crate::typed_array::TaIndexGate::NumericValid(_) => return Ok(DeleteOutcome::NonConfigurable),
|
||||
crate::typed_array::TaIndexGate::NumericInvalid => {
|
||||
vm.sync_global_builtin_mirror(obj, key_si, JsValue::undefined());
|
||||
return DeleteOutcome::Missing;
|
||||
return Ok(DeleteOutcome::Missing);
|
||||
}
|
||||
crate::typed_array::TaIndexGate::Ordinary => {}
|
||||
}
|
||||
@@ -355,21 +372,21 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
|
||||
let meta = obj.prop_meta_at(index);
|
||||
// 已是 hole 视为不存在;非 configurable 不可删。
|
||||
if meta.is_some_and(|m| m.is_hole()) {
|
||||
return DeleteOutcome::Missing;
|
||||
return Ok(DeleteOutcome::Missing);
|
||||
}
|
||||
if meta.is_some_and(|m| !m.attributes.configurable()) {
|
||||
return DeleteOutcome::NonConfigurable;
|
||||
return Ok(DeleteOutcome::NonConfigurable);
|
||||
}
|
||||
obj.mark_hole_at(index);
|
||||
obj.bump_generation();
|
||||
return DeleteOutcome::Deleted;
|
||||
return Ok(DeleteOutcome::Deleted);
|
||||
}
|
||||
// 大索引越出元素区时是命名属性(shape 链),落穿下方查删路径。
|
||||
}
|
||||
// 非下标键:length 是虚拟属性(无 shape 槽、不在元素区),但描述符声明
|
||||
// configurable:false,删除恒失败。
|
||||
if key_si == vm.perm_intern("length") {
|
||||
return DeleteOutcome::NonConfigurable;
|
||||
return Ok(DeleteOutcome::NonConfigurable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +400,7 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
|
||||
// 属性缺失:delete 按规范返 true,镜像槽同步 undefined 保持
|
||||
// "槽 = A 侧原始存储"不变式(与入口预载缺位语义幂等)。
|
||||
vm.sync_global_builtin_mirror(obj, key_si, JsValue::undefined());
|
||||
return DeleteOutcome::Missing;
|
||||
return Ok(DeleteOutcome::Missing);
|
||||
};
|
||||
// walk_own_keys 已返回绝对存储索引(数组含元素区偏移),直接使用。
|
||||
if obj
|
||||
@@ -391,7 +408,7 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
|
||||
.map(|meta| !meta.attributes.configurable())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return DeleteOutcome::NonConfigurable;
|
||||
return Ok(DeleteOutcome::NonConfigurable);
|
||||
}
|
||||
|
||||
// 数组重建前保存元素区(值 + meta),重建后恢复到命名属性之前。
|
||||
@@ -456,13 +473,13 @@ pub fn delete_own_property_outcome<H: VmHost>(vm: &mut H, obj: &mut JsObject, ke
|
||||
// 删除成功:镜像槽同步 undefined(成员形删除 / Reflect.deleteProperty /
|
||||
// 0x9C 清槽同源收口,后两者对此幂等)。
|
||||
vm.sync_global_builtin_mirror(obj, key_si, JsValue::undefined());
|
||||
DeleteOutcome::Deleted
|
||||
Ok(DeleteOutcome::Deleted)
|
||||
}
|
||||
|
||||
/// 删除对象自身属性并投影为布尔(`Reflect.deleteProperty` 语义):成功返回 true,
|
||||
/// 属性不可配置返回 false,恒不抛错。
|
||||
pub fn delete_own_property<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> bool {
|
||||
delete_own_property_outcome(vm, obj, key_si) != DeleteOutcome::NonConfigurable
|
||||
/// 属性不可配置返回 false;deferred namespace 求值触发失败以异常值上抛。
|
||||
pub fn delete_own_property<H: VmHost>(vm: &mut H, obj: &mut JsObject, key_si: u32) -> Result<bool, JsValue> {
|
||||
Ok(delete_own_property_outcome(vm, obj, key_si)? != DeleteOutcome::NonConfigurable)
|
||||
}
|
||||
|
||||
/// JS `Object()` 构造逻辑:创建空对象(prototype 为 null,由 VM 补装内置原型)。
|
||||
@@ -533,7 +550,9 @@ fn namespace_export_get(obj: &JsObject, key_si: u32) -> Result<Option<JsValue>,
|
||||
if is_symbol_key(key_si) {
|
||||
return Ok(None);
|
||||
}
|
||||
match crate::module::module_ns_export(obj, key_si) {
|
||||
// deferred namespace 求值后活值在真实 ns 条目表,读路径按权威对象查询。
|
||||
let target = crate::module::deferred_ns_read_target(obj);
|
||||
match crate::module::module_ns_export(target, key_si) {
|
||||
None => Ok(None),
|
||||
Some(crate::module::ModuleNsQuery::Initialized(v)) => Ok(Some(v)),
|
||||
Some(crate::module::ModuleNsQuery::Uninitialized) => Err(crate::module::NS_UNINITIALIZED_MESSAGE),
|
||||
@@ -580,6 +599,10 @@ pub fn object_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
};
|
||||
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
let owned_keys: Vec<(u32, u32)> = walk_own_keys(vm, obj)
|
||||
.into_iter()
|
||||
.filter(|(_si, offset)| {
|
||||
@@ -685,6 +708,10 @@ pub fn object_assign<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
if source_ptr.is_null() {
|
||||
continue;
|
||||
}
|
||||
// [[OwnPropertyKeys]] 触发(源对象,deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, unsafe { &*source_ptr }) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
let source_keys: Vec<(u32, u32)> = {
|
||||
let source = unsafe { &*source_ptr };
|
||||
walk_own_keys(vm, source)
|
||||
@@ -769,6 +796,15 @@ impl From<JsValue> for DefineDescFailure {
|
||||
pub(crate) fn define_from_descriptor<H: VmHost>(
|
||||
vm: &mut H, obj_ptr: *mut JsObject, key_si: u32, desc_val: JsValue,
|
||||
) -> Result<(), DefineDescFailure> {
|
||||
// [[DefineOwnProperty]] 触发(deferred namespace 先求值,不经 GOP 传导;
|
||||
// symbol-like 键内部 no-op)。触发失败时 uncaught 侧通道已还原为原错误值,
|
||||
// 原值重抛保身份(cyclic 无原值时按文本重建)。
|
||||
if let Err(msg) = vm.ensure_deferred_ns_evaluation(unsafe { &*obj_ptr }, Some(key_si)) {
|
||||
let exc = vm
|
||||
.take_uncaught_value()
|
||||
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
|
||||
return Err(DefineDescFailure::User(exc));
|
||||
}
|
||||
if !desc_val.is_object() {
|
||||
return Err(DefineDescFailure::Engine("Property description must be an object".to_string()));
|
||||
}
|
||||
@@ -1060,6 +1096,16 @@ pub fn object_define_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResul
|
||||
/// # 副作用
|
||||
/// - 在 epoch 分配一个描述符对象
|
||||
fn own_descriptor_of<H: VmHost>(vm: &mut H, obj: &JsObject, key_si: u32) -> Result<Option<JsValue>, JsValue> {
|
||||
// deferred namespace 求值触发([[GetOwnProperty]],symbol-like 键内部 no-op)。
|
||||
// 触发失败时 uncaught 侧通道已还原为原错误值,原值重抛保身份。
|
||||
if let Err(msg) = vm.ensure_deferred_ns_evaluation(obj, Some(key_si)) {
|
||||
let exc = vm
|
||||
.take_uncaught_value()
|
||||
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
|
||||
return Err(exc);
|
||||
}
|
||||
// 求值后描述符读真实 ns(deferred 对象自身槽位是预注册占位,活值在真实 ns)。
|
||||
let obj = crate::module::deferred_ns_read_target(obj);
|
||||
// TA 数值键:界内索引返回元素值 + 常量四字段描述符(writable/enumerable/
|
||||
// configurable 恒真);数字无效与非数字串键落下方槽位路径(真实 own 属性
|
||||
// 原样返回,无属性则 None)。
|
||||
@@ -1194,6 +1240,10 @@ pub fn object_get_own_property_descriptors<H: VmHost>(vm: &mut H, args: &[u8]) -
|
||||
};
|
||||
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
|
||||
// 三段自身键序:字符串/整数键(整键升序在前)后接 Symbol 键插入序。
|
||||
let mut keys = walk_own_keys(vm, obj);
|
||||
@@ -1245,6 +1295,20 @@ fn push_desc_prop(obj: &mut JsObject, shape_forge: &ShapeForge, prop_si: u32, va
|
||||
obj.push_prop(val);
|
||||
}
|
||||
|
||||
/// deferred namespace [[OwnPropertyKeys]] 触发(无键):失败时 uncaught 侧通道
|
||||
/// 已还原为原错误值,原值重抛保身份(cyclic 无原值时按文本重建)。
|
||||
fn ensure_deferred_own_keys<H: VmHost>(vm: &mut H, obj: &JsObject) -> Result<(), JsValue> {
|
||||
match vm.ensure_deferred_ns_evaluation(obj, None) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(msg) => {
|
||||
let exc = vm
|
||||
.take_uncaught_value()
|
||||
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
|
||||
Err(exc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 校验并取出 Object 类方法的目标对象:`args[0]` 为接收者,`args[1]` 为待转换值,
|
||||
/// 缺参或值经 ToObject 失败(null/undefined)时抛 TypeError。
|
||||
fn require_obj_arg<H: VmHost>(vm: &mut H, args: &[u8], fn_name: &str) -> Result<*mut JsObject, JsValue> {
|
||||
@@ -1313,6 +1377,10 @@ pub fn object_freeze<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Ok(val);
|
||||
}
|
||||
let obj_ptr = val.as_js_object_ptr();
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, unsafe { &*obj_ptr }) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
{
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// 模块命名空间导出保持 writable:true,冻结须失败(规范 SetIntegrityLevel)。
|
||||
@@ -1363,6 +1431,10 @@ pub fn object_seal<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Ok(val);
|
||||
}
|
||||
let obj_ptr = val.as_js_object_ptr();
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, unsafe { &*obj_ptr }) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
{
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let keys = walk_own_keys(vm, obj);
|
||||
@@ -1418,6 +1490,10 @@ pub fn object_is_frozen<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
}
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
if obj.is_frozen() {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
}
|
||||
@@ -1454,6 +1530,10 @@ pub fn object_is_sealed<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
}
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
if obj.is_sealed() {
|
||||
return NativeResult::Ok(JsValue::bool(true));
|
||||
}
|
||||
@@ -1500,6 +1580,10 @@ pub fn object_get_own_property_names<H: VmHost>(vm: &mut H, args: &[u8]) -> Nati
|
||||
let obj_ptr = native_try!(require_obj_arg(vm, args, "getOwnPropertyNames"));
|
||||
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
let keys = walk_own_keys(vm, obj);
|
||||
let n = keys.len();
|
||||
let array_proto = vm.builtin_proto(ProtoKind::ArrayProto);
|
||||
@@ -2121,6 +2205,10 @@ pub fn object_proto_define_getter<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeR
|
||||
pub fn object_entries<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj_ptr = native_try!(require_obj_arg(vm, args, "entries"));
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
let keys = walk_own_keys(vm, obj);
|
||||
let owned_keys: Vec<(u32, u32)> = keys
|
||||
.into_iter()
|
||||
@@ -2296,6 +2384,10 @@ pub fn object_group_by<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
pub fn object_values<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
let obj_ptr = native_try!(require_obj_arg(vm, args, "values"));
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(exc) = ensure_deferred_own_keys(vm, obj) {
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
let keys = walk_own_keys(vm, obj);
|
||||
let owned_keys: Vec<(u32, u32)> = keys
|
||||
.into_iter()
|
||||
|
||||
@@ -157,7 +157,11 @@ pub fn reflect_delete_property<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResu
|
||||
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
|
||||
};
|
||||
let target = unsafe { &mut *target_ptr };
|
||||
NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si)))
|
||||
match delete_own_property(vm, target, key_si) {
|
||||
Ok(deleted) => NativeResult::Ok(JsValue::bool(deleted)),
|
||||
// deferred namespace 求值触发失败(cyclic / 失败缓存重抛)原值上抛。
|
||||
Err(exc) => NativeResult::Err(exc),
|
||||
}
|
||||
}
|
||||
|
||||
/// `Reflect.get(target, key, receiver)`:读取属性(含原型链与 accessor)。
|
||||
@@ -213,7 +217,11 @@ pub fn reflect_has<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
Ok(si) => si,
|
||||
Err(e) => return NativeResult::Err(from_engine_error(vm, &e)),
|
||||
};
|
||||
NativeResult::Ok(JsValue::bool(vm.has_property(unsafe { &*target_ptr }, key_si)))
|
||||
// deferred namespace 目标先触发求值(cyclic 抛错 / 失败缓存重抛)。
|
||||
match vm.has_property(unsafe { &*target_ptr }, key_si) {
|
||||
Ok(found) => NativeResult::Ok(JsValue::bool(found)),
|
||||
Err(msg) => NativeResult::Err(from_engine_error(vm, &msg)),
|
||||
}
|
||||
}
|
||||
|
||||
/// `Reflect.isExtensible(target)`:对象是否可扩展。
|
||||
@@ -240,6 +248,13 @@ pub fn reflect_own_keys<H: VmHost>(vm: &mut H, args: &[u8]) -> NativeResult {
|
||||
return type_error(vm, "Reflect.ownKeys target is not an object");
|
||||
};
|
||||
let target = unsafe { &*target_ptr };
|
||||
// [[OwnPropertyKeys]] 触发(deferred namespace 先求值,cyclic 抛错)。
|
||||
if let Err(msg) = vm.ensure_deferred_ns_evaluation(target, None) {
|
||||
let exc = vm
|
||||
.take_uncaught_value()
|
||||
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
|
||||
return NativeResult::Err(exc);
|
||||
}
|
||||
|
||||
// Symbol 键分流在消费端追加:底层字符串枚举源(Object.keys / JSON / for-in)
|
||||
// 语义不变,不泄漏 Symbol 键。
|
||||
|
||||
@@ -335,6 +335,14 @@ fn fill_object_props<H: VmHost>(
|
||||
vm: &mut H, state: &mut CloneState, src: &JsObject, clone_ptr: *mut JsObject,
|
||||
) -> Result<(), JsValue> {
|
||||
let src_val = JsValue::from_js_object(src as *const JsObject as *mut JsObject);
|
||||
// [[OwnPropertyKeys]] 触发(源对象,deferred namespace 先求值,cyclic 抛错)。
|
||||
// plain 对象判定已排除 module namespace,此触发对实际可达路径恒 no-op。
|
||||
if let Err(msg) = vm.ensure_deferred_ns_evaluation(src, None) {
|
||||
let exc = vm
|
||||
.take_uncaught_value()
|
||||
.unwrap_or_else(|| crate::error::create_from_text(vm, &msg));
|
||||
return Err(exc);
|
||||
}
|
||||
let str_keys = walk_own_keys(vm, src);
|
||||
let sym_keys = walk_own_symbol_keys(vm, src);
|
||||
for (si, store) in str_keys {
|
||||
|
||||
@@ -248,7 +248,10 @@ pub trait VmHost {
|
||||
fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue>;
|
||||
/// 存在性判定(规范 HasProperty):自身与原型链任一层 P 为自有属性即存在;
|
||||
/// 数组元素区 hole 视同缺失,TypedArray 整数索引按视图长度判在界。
|
||||
fn has_property(&self, obj: &JsObject, prop_name_si: u32) -> bool;
|
||||
///
|
||||
/// deferred namespace 目标先触发 `EnsureDeferredNamespaceEvaluation`(cyclic
|
||||
/// 守卫抛 TypeError / 失败缓存原值重抛),触发失败以 `Err` 返回。
|
||||
fn has_property(&mut self, obj: &JsObject, prop_name_si: u32) -> Result<bool, String>;
|
||||
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32>;
|
||||
|
||||
// 属性访问
|
||||
@@ -284,6 +287,30 @@ pub trait VmHost {
|
||||
|
||||
// 调用基础设施
|
||||
fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result<JsValue, String>;
|
||||
/// deferred namespace 求值触发(`EnsureDeferredNamespaceEvaluation`):
|
||||
/// 对 deferred 且未求值的 module namespace 同步执行依赖模块函数,缓存结果
|
||||
/// 命名空间或失败错误对象。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 非 module namespace / 非 deferred / 无状态盒 → no-op(`Ok`)。
|
||||
/// 2. symbol-like 键(Symbol 或 deferred 的 `"then"`)→ no-op(走 Ordinary* 路径)。
|
||||
/// 3. 已求值:失败缓存原值重抛(`Err`),成功 no-op。
|
||||
/// 4. cyclic 守卫:`[[Module]]` 为 undefined 哨兵且标记在场 → TypeError;
|
||||
/// 标记等于 `[[Module]]` 函数(自求值中)→ TypeError。
|
||||
/// 5. 求值:`call_function_sync([[Module]])`,成功缓存 `[[Namespace]]`,
|
||||
/// 失败缓存原错误值(还原 uncaught 侧通道供调用方原值重抛)。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `key_si` 为 `None` 表示 `[[OwnPropertyKeys]]` 触发(无键,恒触发)。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 写状态盒 `[[Evaluated]]` / `[[Namespace]]` / 失败错误缓存;可能执行用户
|
||||
/// 模块代码(含分配与 GC)。
|
||||
fn ensure_deferred_ns_evaluation(&mut self, obj: &JsObject, key_si: Option<u32>) -> Result<(), String>;
|
||||
/// 读「正在求值模块」cyclic 守卫标记(`None` = 无模块求值中)。
|
||||
fn evaluating_module(&self) -> Option<JsValue>;
|
||||
/// 置「正在求值模块」cyclic 守卫标记(save/restore 由调用方负责)。
|
||||
fn set_evaluating_module(&mut self, val: Option<JsValue>);
|
||||
/// 构造调用(Construct(C, args)):native 构造器以新对象为 receiver 值传递调用,
|
||||
/// bytecode 构造器压构造帧执行(含 derived 构造器 super() 语义与 new.target
|
||||
/// 传播),返回值非对象时回退到新对象。
|
||||
|
||||
@@ -70,9 +70,11 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
|
||||
|
||||
// well-known symbol 以符号原语绑定,下标与内建符号表(0..WELL_KNOWN_SYMBOL_COUNT)
|
||||
// 一一对应;名称表是 id/名映射的唯一来源,属性名去掉 `Symbol.` 前缀。
|
||||
// 原语按 (realm 编号, 局部下标) 编码,与键空间 `encode_symbol_key` 同口径,
|
||||
// 非零 realm 下与键物化值保持同一符号。
|
||||
for (id, full_name) in oxide_types::private_key::WELL_KNOWN_SYMBOL_NAMES.iter().enumerate() {
|
||||
let name = full_name.strip_prefix("Symbol.").unwrap_or(full_name);
|
||||
bind_well_known_symbol(core, ctor, name, JsValue::symbol(id as u32));
|
||||
bind_well_known_symbol(core, ctor, name, JsValue::symbol_realm(realm_id, id as u32));
|
||||
}
|
||||
|
||||
bind_constructor!(core, global, "Symbol", ctor_ptr, oxide_builtins::symbol::symbol_constructor::<crate::vm::Vm>, 0, hash: true);
|
||||
|
||||
@@ -283,6 +283,11 @@ impl Vm {
|
||||
self.pc += 1;
|
||||
let value = self.regs[a];
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
// 规范 PrivateFieldAdd 步 1:对象不可扩展时拒加私有字段(模块命名空间
|
||||
// 等 exotic 对象创建起即 non-extensible)。
|
||||
if !obj.is_extensible() {
|
||||
return self.raise_type_error("Cannot add private field to a non-extensible object");
|
||||
}
|
||||
if self
|
||||
.kernel_core
|
||||
.shape_forge()
|
||||
@@ -720,7 +725,11 @@ impl Vm {
|
||||
// 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);严格模式分派见
|
||||
// finish_member_delete。
|
||||
let outcome = oxide_builtins::object::delete_own_property_outcome(self, obj, prop_name_si);
|
||||
self.finish_member_delete(rd, outcome)
|
||||
match outcome {
|
||||
// deferred namespace 求值触发失败(cyclic / 失败缓存重抛)原值重抛。
|
||||
Err(exc) => self.raise_captured(exc).map(|_| true),
|
||||
Ok(outcome) => self.finish_member_delete(rd, outcome),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_delete_prop_dynamic(&mut self, rd: usize, b: usize) -> Result<bool, String> {
|
||||
@@ -738,7 +747,11 @@ impl Vm {
|
||||
// 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);严格模式分派见
|
||||
// finish_member_delete。
|
||||
let outcome = oxide_builtins::object::delete_own_property_outcome(self, obj, prop_name_si);
|
||||
self.finish_member_delete(rd, outcome)
|
||||
match outcome {
|
||||
// deferred namespace 求值触发失败(cyclic / 失败缓存重抛)原值重抛。
|
||||
Err(exc) => self.raise_captured(exc).map(|_| true),
|
||||
Ok(outcome) => self.finish_member_delete(rd, outcome),
|
||||
}
|
||||
}
|
||||
|
||||
/// 成员形 delete 的结果分派:不可配置属性在严格模式抛 TypeError(规范要求
|
||||
@@ -794,8 +807,11 @@ impl Vm {
|
||||
// SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。
|
||||
let obj = unsafe { &mut *global_ptr };
|
||||
// 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);不可配置返回
|
||||
// false 且属性保留。
|
||||
let deleted = oxide_builtins::object::delete_own_property(self, obj, si);
|
||||
// false 且属性保留。全局对象非 deferred namespace,触发恒 no-op。
|
||||
let deleted = match oxide_builtins::object::delete_own_property(self, obj, si) {
|
||||
Ok(d) => d,
|
||||
Err(exc) => return self.raise_captured(exc),
|
||||
};
|
||||
// 仅删除成功才清镜像槽(无槽 0 不写,reg 0 是返回槽不可误写)。
|
||||
if deleted && slot > 0 {
|
||||
self.regs[slot] = JsValue::undefined();
|
||||
|
||||
@@ -126,9 +126,11 @@ impl Vm {
|
||||
/// 使外围 try/catch 可捕获(native 函数抛错时原值存于 `last_uncaught_value`)。
|
||||
///
|
||||
/// # 注意事项
|
||||
/// 仅在主 dispatch(`native_call_depth == 0`)下展开——此时 try_stack 只含当前
|
||||
/// 主 dispatch(`native_call_depth == 0`)就地展开——此时 try_stack 只含当前
|
||||
/// 字节码的处理器,展开后 pc/regs[0] 不会被中途的原生调用栈覆盖。原生 builtin
|
||||
/// 内部(depth > 0)必须传播错误,由其调用边界(dispatch_native_call)转换。
|
||||
/// 内部(depth > 0)传播错误,由其调用边界(dispatch_native_call)转换;异常值
|
||||
/// 同步物化入异常通道(不取 uncaught 侧通道,builtin 边界 `from_engine_error`
|
||||
/// 仍须从侧通道还原原值),供内联字节码体在状态还原前由体内 try 处理器捕获。
|
||||
pub(crate) fn raise_call_error(&mut self, err: &str) -> Result<JsValue, String> {
|
||||
if self.native_call_depth == 0 {
|
||||
let exc = self
|
||||
@@ -141,27 +143,34 @@ impl Vm {
|
||||
self.unwind()?;
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
let exc = self
|
||||
.last_uncaught_value
|
||||
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, err));
|
||||
let kind = self.thrown_error_kind(exc);
|
||||
self.exception_value = Some(exc);
|
||||
self.pending_error_kind = Some(kind);
|
||||
Err(err.to_string())
|
||||
}
|
||||
|
||||
/// 恢复已捕获的原始异常值(强转失败载荷)并走异常展开,保原值 kind:
|
||||
/// 深度 0 置原值入异常通道就地展开到外围 catch;深度 >0 返回 kind 前缀
|
||||
/// 文本 `Err`,由原生调用边界恢复为异常对象。
|
||||
/// 文本 `Err`,由原生调用边界恢复为异常对象,异常值同步物化入异常通道
|
||||
/// (供内联字节码体在状态还原前由体内 try 处理器捕获)。
|
||||
///
|
||||
/// # 步骤
|
||||
/// 1. 深度 0:原值与 kind 写入异常通道,就地 `unwind`。
|
||||
/// 2. 深度 >0:以 `Kind: message` 文本 `Err` 返回,不就地展开。
|
||||
/// 2. 深度 >0:原值与 kind 写入异常通道,以 `Kind: message` 文本 `Err` 返回。
|
||||
///
|
||||
/// # 边界与前提
|
||||
/// - `exc` 为调用方已提取的原始异常值;本入口不重取 uncaught 槽。
|
||||
///
|
||||
/// # 副作用
|
||||
/// - 深度 0 写 `exception_value`/`pending_error_kind`,pc 经展开改写。
|
||||
/// - 写 `exception_value`/`pending_error_kind`;深度 0 时 pc 经展开改写。
|
||||
pub(crate) fn raise_captured(&mut self, exc: JsValue) -> Result<(), String> {
|
||||
let kind = self.thrown_error_kind(exc);
|
||||
self.exception_value = Some(exc);
|
||||
self.pending_error_kind = Some(kind);
|
||||
if self.native_call_depth == 0 {
|
||||
let kind = self.thrown_error_kind(exc);
|
||||
self.exception_value = Some(exc);
|
||||
self.pending_error_kind = Some(kind);
|
||||
return self.unwind();
|
||||
}
|
||||
Err(oxide_builtins::typed_array::element_error_text(self, exc))
|
||||
|
||||
@@ -242,6 +242,11 @@ pub struct Vm {
|
||||
/// String 后,for-of 需要重新抛出原值。放在 VM 顶层字段(不在 InlineSyncState 中)
|
||||
/// 以便跨内联调用的恢复过程存活。
|
||||
pub(crate) last_uncaught_value: Option<JsValue>,
|
||||
/// 「正在求值模块」cyclic 守卫标记(deferred namespace 求值触发用):
|
||||
/// `run` 在 dispatch 前置 `Some(undefined)`(入口模块哨兵)、`module_eval`
|
||||
/// 在 `call_function_sync` 前置 `Some(fn)`,两者均 save/restore(嵌套安全)。
|
||||
/// 触发点据此判自求值/祖先求值中的 cyclic 并抛 TypeError。
|
||||
pub(crate) evaluating_module: Option<JsValue>,
|
||||
/// 数组 length define 强转期用户代码抛出的原始异常:与 `last_uncaught_value`
|
||||
/// 分离,避免入口误取其它操作忽略调用残留的值。仅 `define_array_length` 写入,
|
||||
/// 由 Object/Reflect define 入口消费。
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//! 数组下标反解、非对象基删除与属性解析及自身属性槽查找。
|
||||
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_runtime_api::VmHost;
|
||||
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::private_key::{encode_symbol_key, int_key_value, is_int_key, make_int_key, INT_KEY_COUNT};
|
||||
@@ -212,25 +213,35 @@ impl Vm {
|
||||
/// 与 `resolve_property` 的差异:原型链上每层都检查数组元素区
|
||||
/// (`resolve_property` 只在顶层检查元素区,链上仅走 shape 槽),
|
||||
/// 继承自父数组的索引属性在此判存在。
|
||||
pub(crate) fn has_property(&self, obj: &JsObject, prop_name_si: u32) -> bool {
|
||||
///
|
||||
/// deferred namespace 目标在原型链每层触发 `EnsureDeferredNamespaceEvaluation`
|
||||
/// (`in` 运算符逐级走 HasProperty;非 deferred 对象触发为 no-op),触发失败
|
||||
/// (cyclic 守卫抛 TypeError / 失败缓存原值重抛)走可捕获异常展开。
|
||||
pub(crate) fn has_property(&mut self, obj: &JsObject, prop_name_si: u32) -> Result<bool, String> {
|
||||
let length_si = self.length_si;
|
||||
let mut current = Some(obj);
|
||||
let mut depth = 0usize;
|
||||
while let Some(obj) = current {
|
||||
// deferred namespace 求值触发(symbol-like 键内部 no-op):链上每层
|
||||
// 独立判定,`key in obj`(obj 原型为 deferred ns)在 ns 层触发。
|
||||
if let Err(msg) = self.ensure_deferred_ns_evaluation(obj, Some(prop_name_si)) {
|
||||
self.raise_call_error(&msg)?;
|
||||
return Ok(false);
|
||||
}
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
// 统一数值键门(exotic [[HasProperty]]):同 `ordinary_get_inner`
|
||||
// 的口径,链上每层同口径判定。
|
||||
if obj.is_typed_array_obj() {
|
||||
match oxide_builtins::typed_array::ta_index_gate(self, obj, prop_name_si) {
|
||||
oxide_builtins::typed_array::TaIndexGate::NumericValid(_) => return true,
|
||||
oxide_builtins::typed_array::TaIndexGate::NumericInvalid => return false,
|
||||
oxide_builtins::typed_array::TaIndexGate::NumericValid(_) => return Ok(true),
|
||||
oxide_builtins::typed_array::TaIndexGate::NumericInvalid => return Ok(false),
|
||||
oxide_builtins::typed_array::TaIndexGate::Ordinary => {}
|
||||
}
|
||||
}
|
||||
if self.get_own_property_slot(obj, prop_name_si).is_some() {
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
if depth >= MAX_PROTO_CHAIN_DEPTH {
|
||||
break;
|
||||
@@ -239,7 +250,7 @@ impl Vm {
|
||||
let proto = obj.proto();
|
||||
current = proto.is_object().then(|| unsafe { &*proto.as_js_object_ptr() });
|
||||
}
|
||||
false
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// 查找自身属性槽下标:数组 length 虚拟属性返回 `None`,元素区返回下标
|
||||
|
||||
@@ -209,7 +209,7 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
|
||||
self.resolve_property(obj, prop_name_si)
|
||||
}
|
||||
fn has_property(&self, obj: &JsObject, prop_name_si: u32) -> bool {
|
||||
fn has_property(&mut self, obj: &JsObject, prop_name_si: u32) -> Result<bool, String> {
|
||||
self.has_property(obj, prop_name_si)
|
||||
}
|
||||
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32> {
|
||||
@@ -252,6 +252,71 @@ impl oxide_runtime_api::VmHost for Vm {
|
||||
fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result<JsValue, String> {
|
||||
self.call_function_sync(callee, receiver, args)
|
||||
}
|
||||
fn ensure_deferred_ns_evaluation(&mut self, obj: &JsObject, key_si: Option<u32>) -> Result<(), String> {
|
||||
// 非 deferred module namespace 直接 no-op(热路径零成本)。
|
||||
if !obj.is_module_namespace() || !obj.is_deferred() {
|
||||
return Ok(());
|
||||
}
|
||||
// symbol-like 键(Symbol 或 deferred 的 "then")走 Ordinary* 路径,不触发。
|
||||
if let Some(key) = key_si {
|
||||
if oxide_types::private_key::is_symbol_key(key) {
|
||||
return Ok(());
|
||||
}
|
||||
if key == self.perm_intern("then") {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
// 读状态盒(经 native_data 裸指针,Box 堆分配 GC 不搬移,指针稳定)。
|
||||
let Some(state) = oxide_builtins::module::deferred_state_mut(obj) else {
|
||||
return Ok(());
|
||||
};
|
||||
if state.evaluated {
|
||||
// 已求值:失败缓存原值重抛(保错误对象身份),成功 no-op。
|
||||
if let Some(err) = state.error {
|
||||
// 还原 uncaught 侧通道为缓存的原错误值,调用方([[Get]] 等)
|
||||
// 经 raise_call_error 原值重抛,保 sameValue 身份。
|
||||
self.last_uncaught_value = Some(err);
|
||||
return Err(self.error_text(err));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// cyclic 守卫:[[Module]] 为 undefined 哨兵(自导入/祖先 defer)时,
|
||||
// 标记在场即求值中 → TypeError;标记已清(祖先已求值完毕)按已求值处理。
|
||||
if state.module.is_undefined() {
|
||||
if self.evaluating_module.is_some() {
|
||||
return Err(self.error_message_text("TypeError", "cyclic module evaluation"));
|
||||
}
|
||||
state.evaluated = true;
|
||||
return Ok(());
|
||||
}
|
||||
// [[Module]] 为函数:标记等于本函数即自求值中(安全网)→ TypeError。
|
||||
if self.evaluating_module == Some(state.module) {
|
||||
return Err(self.error_message_text("TypeError", "cyclic module evaluation"));
|
||||
}
|
||||
// EvaluateSync:调依赖模块函数,缓存结果命名空间或失败错误。
|
||||
match self.call_function_sync(state.module, JsValue::undefined(), &[]) {
|
||||
Ok(ns) => {
|
||||
state.namespace = ns;
|
||||
state.evaluated = true;
|
||||
}
|
||||
Err(text) => {
|
||||
// 原错误值经 uncaught 侧通道取走存入状态盒,并还原侧通道供
|
||||
// 调用方([[Get]] 等)原值重抛。
|
||||
let exc = self.last_uncaught_value.take();
|
||||
state.error = exc;
|
||||
state.evaluated = true;
|
||||
self.last_uncaught_value = exc;
|
||||
return Err(text);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn evaluating_module(&self) -> Option<JsValue> {
|
||||
self.evaluating_module
|
||||
}
|
||||
fn set_evaluating_module(&mut self, val: Option<JsValue>) {
|
||||
self.evaluating_module = val;
|
||||
}
|
||||
fn construct_ctor(&mut self, ctor: JsValue, args: &[JsValue]) -> Result<JsValue, JsValue> {
|
||||
Vm::construct_ctor(self, ctor, args)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::vm::{Completion, FrameArgs, FrameContinuation, PendingAsyncEscape, TryHandler, Vm};
|
||||
use crate::vm_trace;
|
||||
use oxide_bytecode::opcode;
|
||||
use oxide_runtime_api::VmHost;
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::private_key::make_private_name_id;
|
||||
use oxide_types::value::JsValue;
|
||||
@@ -233,6 +234,12 @@ impl Vm {
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
let value = self.regs[a];
|
||||
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
|
||||
// [[DefineOwnProperty]] 触发(类字段初始化 CreateDataPropertyOrThrow 路径):
|
||||
// deferred namespace 先求值,触发失败走可捕获异常展开。
|
||||
if let Err(msg) = self.ensure_deferred_ns_evaluation(obj, Some(prop_name_si)) {
|
||||
self.raise_call_error(&msg)?;
|
||||
return Ok(());
|
||||
}
|
||||
match self.define_data_property(obj, prop_name_si, value, PropAttributes::DEFAULT_DATA) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(msg) => self.raise_error_kind("TypeError", &msg),
|
||||
@@ -251,6 +258,11 @@ impl Vm {
|
||||
let prop_name_si = self.property_key_si(self.regs[b])?;
|
||||
let value = self.regs[a];
|
||||
let obj = unsafe { &mut *obj_val.as_js_object_ptr() };
|
||||
// [[DefineOwnProperty]] 触发(同 `dispatch_define_prop`,带描述符形态)。
|
||||
if let Err(msg) = self.ensure_deferred_ns_evaluation(obj, Some(prop_name_si)) {
|
||||
self.raise_call_error(&msg)?;
|
||||
return Ok(());
|
||||
}
|
||||
match self.define_data_property(obj, prop_name_si, value, PropAttributes(attrs)) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(msg) => self.raise_error_kind("TypeError", &msg),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::vm::{ForInIter, FrameArgs, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH};
|
||||
use crate::vm_trace;
|
||||
use oxide_runtime_api::{push_units_to, to_boolean, to_units_full};
|
||||
use oxide_runtime_api::{push_units_to, to_boolean, to_units_full, VmHost};
|
||||
use oxide_types::object::{JsObject, PropAttributes};
|
||||
use oxide_types::private_key::{
|
||||
encode_symbol_key, int_key_value, is_int_key, is_private_name_key, is_symbol_key, make_int_key, INT_KEY_COUNT,
|
||||
@@ -453,8 +453,8 @@ impl Vm {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let prop_name_si = self.property_key_si(key_val)?;
|
||||
// `in` 即 HasProperty:原型链各层都要看数组元素区与 TypedArray 整数索引,
|
||||
// 读值解析不覆盖这两处。
|
||||
let found = self.has_property(obj, prop_name_si);
|
||||
// 读值解析不覆盖这两处。deferred namespace 目标先触发求值(cyclic 抛错)。
|
||||
let found = self.has_property(obj, prop_name_si)?;
|
||||
self.regs[rd] = JsValue::bool(found);
|
||||
Ok(())
|
||||
}
|
||||
@@ -473,6 +473,17 @@ impl Vm {
|
||||
obj_val = oxide_runtime_api::to_object(obj_val, self)?;
|
||||
}
|
||||
|
||||
// [[OwnPropertyKeys]] 触发(for-in 枚举源):deferred namespace 先求值,
|
||||
// cyclic 守卫抛 TypeError,触发失败走可捕获异常展开。
|
||||
let obj_ptr = obj_val.as_js_object_ptr();
|
||||
if !obj_ptr.is_null() {
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
if let Err(msg) = self.ensure_deferred_ns_evaluation(obj, None) {
|
||||
self.raise_call_error(&msg)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut keys_vec: Vec<(JsValue, u32)> = Vec::new();
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
let mut current = obj_val;
|
||||
@@ -569,7 +580,8 @@ impl Vm {
|
||||
// 模块命名空间 exotic:EnumerateObjectProperties 构建键表时逐键走
|
||||
// `? [[GetOwnProperty]]`,未初始化导出抛 ReferenceError。校验须在排序与
|
||||
// 迭代器建立之前(INIT 期),不遗留 for-in 迭代器;非 live ns 无条目表,
|
||||
// `module_ns_export` 返回 None,落普通路径零行为变化。
|
||||
// `module_ns_export` 返回 None,落普通路径零行为变化。deferred 对象求值后
|
||||
// 校验按真实 ns 条目表(deferred 自身槽位是预注册占位)。
|
||||
if obj_val.is_object() {
|
||||
let ns_obj = unsafe { &*obj_val.as_js_object_ptr() };
|
||||
if ns_obj.is_module_namespace() {
|
||||
@@ -577,8 +589,9 @@ impl Vm {
|
||||
if is_symbol_key(*si) {
|
||||
continue;
|
||||
}
|
||||
let target = oxide_builtins::module::deferred_ns_read_target(ns_obj);
|
||||
if let Some(oxide_builtins::module::ModuleNsQuery::Uninitialized) =
|
||||
oxide_builtins::module::module_ns_export(ns_obj, *si)
|
||||
oxide_builtins::module::module_ns_export(target, *si)
|
||||
{
|
||||
return self
|
||||
.raise_error_kind("ReferenceError", oxide_builtins::module::NS_UNINITIALIZED_MESSAGE);
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::{ic_trace, vm_trace};
|
||||
use oxide_kernel::prop_forge::PropTemplate;
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
use oxide_runtime_api as coercion;
|
||||
use oxide_runtime_api::VmHost;
|
||||
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
@@ -54,7 +55,15 @@ impl Vm {
|
||||
// 模块命名空间 exotic [[Get]]:条目表是权威状态,预注册但未初始化的
|
||||
// 导出读抛 ReferenceError;非 live ns(无表)落普通属性路径零行为变化。
|
||||
if obj.is_module_namespace() {
|
||||
if let Some(state) = oxide_builtins::module::module_ns_export(obj, prop_name_si) {
|
||||
// deferred namespace 求值触发(symbol-like 键内部 no-op);触发
|
||||
// 失败(cyclic / 失败缓存重抛)走可捕获异常展开,外围 try/catch
|
||||
// 可收到原错误值。
|
||||
if let Err(msg) = self.ensure_deferred_ns_evaluation(obj, Some(prop_name_si)) {
|
||||
return self.raise_call_error(&msg);
|
||||
}
|
||||
// 求值后活值在真实 ns 条目表,deferred 对象自身槽位是预注册占位。
|
||||
let target = oxide_builtins::module::deferred_ns_read_target(obj);
|
||||
if let Some(state) = oxide_builtins::module::module_ns_export(target, prop_name_si) {
|
||||
return match state {
|
||||
oxide_builtins::module::ModuleNsQuery::Initialized(v) => Ok(v),
|
||||
oxide_builtins::module::ModuleNsQuery::Uninitialized => {
|
||||
@@ -360,8 +369,9 @@ impl Vm {
|
||||
prop_name_si,
|
||||
use_frame_push
|
||||
);
|
||||
// 模块命名空间 exotic [[Set]] 恒 false(规范 10.4.6.8):严格抛 TypeError,
|
||||
// sloppy 静默 no-op;Reflect.set 以 strict=true 调用并投影为 false。
|
||||
// 模块命名空间 exotic [[Set]] 恒 false(规范 10.4.6.8,deferred 形态同口径
|
||||
// 不触发求值):严格抛 TypeError,sloppy 静默 no-op;Reflect.set 以
|
||||
// strict=true 调用并投影为 false。
|
||||
if obj.is_module_namespace() {
|
||||
if strict {
|
||||
return self.write_protection_failure(builtin, "Cannot assign to a module namespace export");
|
||||
@@ -1713,7 +1723,7 @@ mod tests {
|
||||
assert_eq!(obj.logical_len(), 2_000_001, "length 扩到 index+1");
|
||||
assert_eq!(obj.array_prop_count, 1, "物理区不物化");
|
||||
assert_eq!(vm.ordinary_get(obj, si, a).expect("get"), JsValue::int(9));
|
||||
assert!(vm.has_property(obj, si), "in 为 true");
|
||||
assert!(vm.has_property(obj, si).expect("has_property"), "in 为 true");
|
||||
let keys = oxide_builtins::object::walk_own_keys(&vm, obj);
|
||||
assert!(keys.iter().any(|(k, _)| *k == si), "枚举含大索引键");
|
||||
}
|
||||
@@ -1729,7 +1739,7 @@ mod tests {
|
||||
let obj = unsafe { &*b.as_js_object_ptr() };
|
||||
assert_eq!(obj.logical_len(), 0, "2^32-1 不扩展 length");
|
||||
assert_eq!(vm.ordinary_get(obj, si, b).expect("get"), JsValue::int(9));
|
||||
assert!(vm.has_property(obj, si));
|
||||
assert!(vm.has_property(obj, si).expect("has_property"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1763,7 +1773,7 @@ mod tests {
|
||||
|
||||
let obj = unsafe { &*d.as_js_object_ptr() };
|
||||
assert_eq!(obj.logical_len(), 5);
|
||||
assert!(!vm.has_property(obj, si), "截断后 3000000 键被删");
|
||||
assert!(!vm.has_property(obj, si).expect("has_property"), "截断后 3000000 键被删");
|
||||
assert_eq!(vm.ordinary_get(obj, si, d).expect("get"), JsValue::undefined());
|
||||
}
|
||||
|
||||
@@ -1839,11 +1849,12 @@ mod tests {
|
||||
.expect("set");
|
||||
|
||||
let outcome =
|
||||
oxide_builtins::object::delete_own_property_outcome(&mut vm, unsafe { &mut *a.as_js_object_ptr() }, si);
|
||||
oxide_builtins::object::delete_own_property_outcome(&mut vm, unsafe { &mut *a.as_js_object_ptr() }, si)
|
||||
.expect("delete");
|
||||
assert_eq!(outcome, oxide_builtins::object::DeleteOutcome::Deleted);
|
||||
let obj = unsafe { &*a.as_js_object_ptr() };
|
||||
assert_eq!(obj.logical_len(), 2_000_001, "length 不变");
|
||||
assert!(!vm.has_property(obj, si));
|
||||
assert!(!vm.has_property(obj, si).expect("has_property"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1941,4 +1952,109 @@ mod tests {
|
||||
let obj = unsafe { &*boxed.as_js_object_ptr() };
|
||||
assert_eq!(vm.ordinary_get(obj, length_si, boxed).expect("get"), JsValue::int(4), "length 不变");
|
||||
}
|
||||
|
||||
// ---- deferred namespace 求值触发测试 ----
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 建带编译服务的 VM 与一个依赖模块函数(deferred ns 的 `[[Module]]` 载荷)。
|
||||
fn deferred_vm_with_function() -> (Vm, JsValue) {
|
||||
let mut vm = Vm::new();
|
||||
vm.set_compiler_service(Arc::new(oxide_compiler::DefaultCompilerService));
|
||||
let fn_val = vm
|
||||
.create_dynamic_function(&[], "return 42;", false, false)
|
||||
.expect("create function");
|
||||
(vm, fn_val)
|
||||
}
|
||||
|
||||
/// 建 deferred namespace 对象(`fn_val` 为依赖模块函数,未求值态)。
|
||||
fn make_deferred_ns(vm: &mut Vm, fn_val: JsValue) -> JsValue {
|
||||
vm.set_reg(1, fn_val);
|
||||
vm.set_reg(2, JsValue::undefined());
|
||||
oxide_builtins::module::module_defer_object(vm, &[0, 1, 2]).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deferred_ns_get_triggers_evaluation() {
|
||||
let (mut vm, fn_val) = deferred_vm_with_function();
|
||||
let ns_val = make_deferred_ns(&mut vm, fn_val);
|
||||
let ns_ptr = ns_val.as_js_object_ptr();
|
||||
// 未求值态。
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
assert!(!oxide_builtins::module::deferred_state(ns).unwrap().evaluated, "求值前应为 false");
|
||||
|
||||
// 读属性触发求值(模块函数返回 42,非对象无妨,只求值完成)。
|
||||
let x_si = vm.kernel_core.perm_interner().intern("x").0;
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
let _ = vm.ordinary_get(ns, x_si, ns_val);
|
||||
|
||||
// 求值后 [[Evaluated]] 置真。
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
assert!(oxide_builtins::module::deferred_state(ns).unwrap().evaluated, "求值后应为 true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deferred_ns_cyclic_get_throws_type_error() {
|
||||
let (mut vm, _fn_val) = deferred_vm_with_function();
|
||||
// 哨兵形态:[[Module]] = undefined(自导入/祖先 defer)。
|
||||
vm.set_reg(1, JsValue::undefined());
|
||||
vm.set_reg(2, JsValue::undefined());
|
||||
let ns_val = oxide_builtins::module::module_defer_object(&mut vm, &[0, 1, 2]).unwrap();
|
||||
let ns_ptr = ns_val.as_js_object_ptr();
|
||||
|
||||
// 模拟「正在求值模块」:置 cyclic 守卫标记。
|
||||
vm.set_evaluating_module(Some(JsValue::undefined()));
|
||||
|
||||
// 读属性触发 cyclic 守卫 → TypeError(builtin 上下文返 Err)。
|
||||
let x_si = vm.kernel_core.perm_interner().intern("x").0;
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
let result = vm.ordinary_get(ns, x_si, ns_val);
|
||||
assert!(result.is_err(), "cyclic 应抛 TypeError");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deferred_ns_failure_rethrows_same_error() {
|
||||
let (mut vm, _fn_val) = deferred_vm_with_function();
|
||||
// 模块函数抛错。
|
||||
let throwing_fn = vm
|
||||
.create_dynamic_function(&[], "throw new Error('boom');", false, false)
|
||||
.expect("create function");
|
||||
vm.set_reg(1, throwing_fn);
|
||||
vm.set_reg(2, JsValue::undefined());
|
||||
let ns_val = oxide_builtins::module::module_defer_object(&mut vm, &[0, 1, 2]).unwrap();
|
||||
let ns_ptr = ns_val.as_js_object_ptr();
|
||||
let x_si = vm.kernel_core.perm_interner().intern("x").0;
|
||||
|
||||
// 首次读:触发求值,模块函数抛错,缓存错误。
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
assert!(vm.ordinary_get(ns, x_si, ns_val).is_err(), "首次读应抛错");
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
let cached_err = oxide_builtins::module::deferred_state(ns).unwrap().error.expect("失败应缓存错误");
|
||||
|
||||
// 再读:重抛同一错误对象(sameValue 身份)。
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
assert!(vm.ordinary_get(ns, x_si, ns_val).is_err(), "再读应再抛");
|
||||
// 重抛经 uncaught 侧通道还原,取回验证指针身份。
|
||||
let rethrown = vm.take_uncaught_value().expect("重抛应还原 uncaught 侧通道");
|
||||
assert!(
|
||||
std::ptr::eq(rethrown.as_js_object_ptr(), cached_err.as_js_object_ptr()),
|
||||
"再读应重抛同一错误对象"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deferred_ns_symbol_like_key_does_not_trigger() {
|
||||
let (mut vm, fn_val) = deferred_vm_with_function();
|
||||
let ns_val = make_deferred_ns(&mut vm, fn_val);
|
||||
let ns_ptr = ns_val.as_js_object_ptr();
|
||||
|
||||
// 读 "then" 键(deferred 的 symbol-like 键)不触发求值。
|
||||
let then_si = vm.kernel_core.perm_interner().intern("then").0;
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
let _ = vm.ordinary_get(ns, then_si, ns_val);
|
||||
|
||||
// 求值未触发:[[Evaluated]] 仍为 false。
|
||||
let ns = unsafe { &*ns_ptr };
|
||||
assert!(!oxide_builtins::module::deferred_state(ns).unwrap().evaluated, "symbol-like 键不应触发求值");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +519,26 @@ impl Vm {
|
||||
sub.n_args,
|
||||
sub.is_arrow
|
||||
);
|
||||
let result = self.dispatch();
|
||||
// 体自身 try 处理器基线:异常逃出体内时先由体内处理器捕获
|
||||
// (catch_pc 在体字节码内,状态还原后指向调用方字节码即悬空)。
|
||||
let try_base = self.try_stack.len();
|
||||
let mut result = self.dispatch();
|
||||
loop {
|
||||
if result.is_ok() || self.try_stack.len() <= try_base {
|
||||
break;
|
||||
}
|
||||
// 体内处理器捕获:pc 落体字节码 catch/finally,继续执行体余部。
|
||||
// 捕获体再抛(re-throw)时展开会消费外层处理器并跳外字节码 catch_pc,
|
||||
// 与活动字节码不一致——re-dispatch 返回 Err 即该形态,还原状态后由
|
||||
// 调用边界按外层处理器正常展开(与修复前同口径)。
|
||||
if self.unwind().is_err() {
|
||||
break;
|
||||
}
|
||||
result = self.dispatch();
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.native_call_depth -= 1;
|
||||
|
||||
vm_trace!("call_bytecode: restoring state pc={} result={:?}", saved.pc, result.as_ref().ok());
|
||||
@@ -724,7 +743,14 @@ impl Vm {
|
||||
self.top_level_this = this_val;
|
||||
self.regs[254] = this_val;
|
||||
|
||||
// 「正在求值模块」cyclic 守卫标记:入口模块函数对象在 run 不可得(顶层
|
||||
// CREATE_CLOSURE 产物),用 undefined 哨兵——sentinel 检查只看 is_some(),
|
||||
// 依赖模块的 real-function 检查恒不匹配 undefined,安全。save/restore 保
|
||||
// 嵌套/重入安全,dispatch 返回即还原(微任务 drain 前模块已非求值中)。
|
||||
let saved_evaluating = self.evaluating_module.take();
|
||||
self.evaluating_module = Some(JsValue::undefined());
|
||||
let result = self.execute_task(|vm| vm.dispatch());
|
||||
self.evaluating_module = saved_evaluating;
|
||||
// 顶层执行结束后 drain 微任务队列:Promise reactions 与 thenable 委托在此执行。
|
||||
self.drain_microtasks();
|
||||
// 指令周期采样:run 末聚合本 run 的样本并输出 top-K 直方图(关闭时零开销
|
||||
|
||||
@@ -135,6 +135,7 @@ impl Vm {
|
||||
try_stack: Vec::new(),
|
||||
exception_value: None,
|
||||
last_uncaught_value: None,
|
||||
evaluating_module: None,
|
||||
pending_length_exception: None,
|
||||
pending_exception: None,
|
||||
pending_error_kind: None,
|
||||
@@ -295,6 +296,7 @@ impl Vm {
|
||||
try_stack: Vec::new(),
|
||||
exception_value: None,
|
||||
last_uncaught_value: None,
|
||||
evaluating_module: None,
|
||||
pending_length_exception: None,
|
||||
pending_exception: None,
|
||||
pending_error_kind: None,
|
||||
|
||||
Reference in New Issue
Block a user