mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
feat: 对象层惰性模块命名空间求值触发
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user