mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
1655 lines
78 KiB
Rust
1655 lines
78 KiB
Rust
use crate::vm::{
|
||
Completion, ConstructArgs, DisposeResumeKind, ForInIter, FrameArgs, FrameContinuation, PendingAsyncDispose,
|
||
TryHandler, Vm, MAX_PROTO_CHAIN_DEPTH,
|
||
};
|
||
use crate::vm_trace;
|
||
use oxide_builtins::iterator::is_callable;
|
||
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,
|
||
WELL_KNOWN_SYMBOL_ASYNC_DISPOSE, WELL_KNOWN_SYMBOL_DISPOSE, WELL_KNOWN_SYMBOL_HAS_INSTANCE,
|
||
};
|
||
use oxide_types::value::JsValue;
|
||
|
||
/// 单资源释放结局(规范 Dispose 语义):`Done` = 释放成功;`NoMethod` = 无释放方法
|
||
/// (非对象 / 方法未定义 / 不可调用,跳过);`Err(v)` = 释放方法抛错(v 为原始异常值);
|
||
/// `Suspended(promise)` = 异步释放已挂起(promise 为释放方法结果经 PromiseResolve 包装
|
||
/// 后登记的结算 promise,调用方据此登记 `PendingAsyncDispose`)。
|
||
enum DisposeOutcome {
|
||
Done,
|
||
NoMethod,
|
||
Err(JsValue),
|
||
Suspended(JsValue),
|
||
}
|
||
|
||
/// 批量释放(`dispose_above` / `dispose_top_n`)结局:`Clean` = 全部正常;
|
||
/// `Threw` = 有释放方法抛错(合并错误已写入 `exception_value`);`Suspended` =
|
||
/// 异步挂起已登记(`PendingAsyncDispose` 已置,调用方须快照并早退)。
|
||
pub(crate) enum DisposePhase {
|
||
Clean,
|
||
Threw,
|
||
Suspended,
|
||
}
|
||
|
||
impl Vm {
|
||
pub(crate) fn dispatch_new_expression(&mut self, rd: usize, a: usize, b: usize) -> Result<bool, String> {
|
||
let constructor_reg = a;
|
||
let first_arg_reg = b as u8;
|
||
vm_trace!("NEW_EXPRESSION rd={}", rd);
|
||
|
||
let constructor = self.regs[constructor_reg];
|
||
if !constructor.is_object() {
|
||
return self
|
||
.raise_type_error("NEW_EXPRESSION: constructor is not an object")
|
||
.map(|_| true);
|
||
}
|
||
let ctor_ptr = constructor.as_js_object_ptr();
|
||
if ctor_ptr.is_null() {
|
||
return self.raise_type_error("NEW_EXPRESSION: constructor is null").map(|_| true);
|
||
}
|
||
let ctor_obj = unsafe { &*ctor_ptr };
|
||
if !ctor_obj.is_function() {
|
||
return self
|
||
.raise_type_error("NEW_EXPRESSION: constructor is not a function")
|
||
.map(|_| true);
|
||
}
|
||
if ctor_obj.is_arrow() {
|
||
return self
|
||
.raise_type_error("arrow functions cannot be used as constructors")
|
||
.map(|_| true);
|
||
}
|
||
// native 方法(非构造器)不可 new;bound 包装除外(其构造语义转发到 target)。
|
||
if ctor_obj.native_fn().is_some()
|
||
&& ctor_obj.type_tag != oxide_types::object::JsObject::OBJ_TYPE_CONSTRUCTOR
|
||
&& ctor_obj.type_tag != oxide_types::object::JsObject::OBJ_TYPE_BOUND
|
||
{
|
||
return self.raise_type_error("object is not a constructor").map(|_| true);
|
||
}
|
||
|
||
let ext = self.bytecode[self.pc];
|
||
self.pc += 1;
|
||
let arg_count = (ext & 0xFF) as usize;
|
||
// ext 高 8 位 = 调用点存活上界(0 = 未编码/全量),压帧窗口按此截断。
|
||
let call_window = (ext >> 8) as u8;
|
||
|
||
// bound 包装:解包链后转发到最内层 target([[Construct]] 语义)。
|
||
if ctor_obj.type_tag == oxide_types::object::JsObject::OBJ_TYPE_BOUND {
|
||
let mut args = Vec::with_capacity(arg_count);
|
||
for i in 0..arg_count.min(256) {
|
||
args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]);
|
||
}
|
||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||
let new_obj = self.alloc_object(JsObject::new_empty(
|
||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||
JsValue::from_js_object(proto_ptr),
|
||
));
|
||
return self.dispatch_new_bound(rd, constructor, new_obj, args, call_window);
|
||
}
|
||
|
||
let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject;
|
||
let new_obj = self.alloc_object(JsObject::new_empty(
|
||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||
JsValue::from_js_object(proto_ptr),
|
||
));
|
||
|
||
// GpFC 按构造器种类分读形:native 构造器体(native_fn + CONSTRUCTOR
|
||
// tag)保持裸存储读(占位 receiver 的原型不可观测,可观测面由构造体
|
||
// 自重读覆盖);字节码构造器走传播读(Ordinary [[Construct]] 的 GpFC
|
||
// 先于函数体求值,访问器 getter 的异常须原值上抛,非对象结果回落
|
||
// Object.prototype)。bound 包装已在上方转发臂返回,不到达此处。
|
||
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||
let proto_val = if ctor_obj.native_fn().is_some() {
|
||
self.resolve_property(ctor_obj, proto_si)
|
||
} else {
|
||
let pc_before = self.pc;
|
||
match self.ordinary_get(ctor_obj, proto_si, constructor) {
|
||
Ok(v) if self.pc == pc_before => Some(v),
|
||
Ok(_) => {
|
||
// getter 抛且已有 catch/finally 接手:展开已跳到落点,停构造流。
|
||
return Ok(true);
|
||
}
|
||
Err(err) => {
|
||
// 无处理器(或重入在途异常):恢复原值走异常通道。
|
||
let exc = self
|
||
.last_uncaught_value
|
||
.take()
|
||
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &err));
|
||
self.exception_value = Some(exc);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
return self.unwind().map(|_| true);
|
||
}
|
||
}
|
||
};
|
||
if let Some(v) = proto_val {
|
||
if v.is_object() {
|
||
let new_obj_mut = unsafe { &mut *new_obj };
|
||
let proto_obj_ptr = v.as_js_object_ptr();
|
||
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
|
||
}
|
||
}
|
||
|
||
if ctor_obj.native_fn().is_some() {
|
||
let new_obj_val = JsValue::object(new_obj as *mut u8);
|
||
let mut args = Vec::with_capacity(arg_count);
|
||
for i in 0..arg_count.min(256) {
|
||
args.push(self.regs[first_arg_reg.wrapping_add(i as u8) as usize]);
|
||
}
|
||
// native 构造器:收口到 call_function_sync(与 spread / bound 变体一致)。
|
||
// 该入口统一保存/恢复调用窗口与 253/254 槽且不触碰 255,调用方
|
||
// new.target 与 this 跨 native 构造不被污染;receiver 经 arg0 打包,
|
||
// native 侧经 reg(args[0]) 读取,构造语义不变。newTarget 快照后置入
|
||
// reg(255) 暴露给 native 构造器(调用后恢复原值);构造形态标记同窗
|
||
// 夹持(调用后恢复)。
|
||
let saved_new_target = self.regs[255];
|
||
let saved_constructing = self.constructing_native;
|
||
self.regs[255] = constructor;
|
||
self.constructing_native = true;
|
||
let result = self.call_function_sync(constructor, new_obj_val, &args);
|
||
self.constructing_native = saved_constructing;
|
||
self.regs[255] = saved_new_target;
|
||
match result {
|
||
Ok(v) => {
|
||
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
|
||
Ok(false)
|
||
}
|
||
Err(_) => {
|
||
let exc = self
|
||
.last_uncaught_value
|
||
.take()
|
||
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, "constructor call failed"));
|
||
self.exception_value = Some(exc);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
self.unwind().map(|_| true)
|
||
}
|
||
}
|
||
} else if ctor_obj.sub_module_index() > 0 {
|
||
let sub_idx = ctor_obj.sub_module_index() as usize;
|
||
let sub = match self.callee_module(ctor_obj) {
|
||
Some(m) => m,
|
||
None => {
|
||
// 上界取构造器自身代际平表长度(跨 run 调用时可异于当前代际)。
|
||
return Err(format!(
|
||
"NEW_EXPRESSION: sub_module_index {} out of bounds (max {})",
|
||
sub_idx,
|
||
self.tables.get(&ctor_obj.table_gen()).map(|t| t.modules.len()).unwrap_or(0)
|
||
));
|
||
}
|
||
};
|
||
// 生成器函数不是构造器:`new g()` 抛 TypeError。
|
||
if sub.is_generator {
|
||
return self.raise_type_error("g is not a constructor").map(|_| true);
|
||
}
|
||
// 异步函数不是构造器:`new f()` 抛 TypeError。
|
||
if sub.is_async {
|
||
return self.raise_type_error("g is not a constructor").map(|_| true);
|
||
}
|
||
|
||
let new_obj_val = JsValue::object(new_obj as *mut u8);
|
||
// 收敛到统一压帧入口:derived 构造器在 super() 前 this 为 undefined,
|
||
// 基类构造器 this = 新对象;new.target = 构造器本身。
|
||
let this_value = if ctor_obj.is_derived_constructor() {
|
||
JsValue::undefined()
|
||
} else {
|
||
new_obj_val
|
||
};
|
||
self.push_bytecode_frame(
|
||
constructor,
|
||
this_value,
|
||
FrameArgs::RegRange {
|
||
first: first_arg_reg,
|
||
count: arg_count,
|
||
},
|
||
ConstructArgs::new(rd as u8, new_obj_val, constructor),
|
||
FrameContinuation::None,
|
||
call_window,
|
||
)?;
|
||
Ok(true)
|
||
} else {
|
||
let error = oxide_builtins::error::create_from_text(
|
||
self,
|
||
"NEW_EXPRESSION: bytecode constructors not yet supported",
|
||
);
|
||
self.exception_value = Some(error);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(error));
|
||
self.unwind().map(|_| true)
|
||
}
|
||
}
|
||
|
||
/// bound 函数构造分支:按 bound [[Construct]] 语义解包链后转发到最内层 target。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 逐层解包 [[BoundTargetFunction]]:每层绑定实参(状态对象存储槽 2+)
|
||
/// 拼到调用实参之前(外层先解包 → 最终顺序为内层绑定实参先、外层后、
|
||
/// 调用实参尾)
|
||
/// 2. 校验最内层 target 可构造(native 须 CONSTRUCTOR 标记;字节码须非
|
||
/// arrow/async/generator),否则抛 TypeError
|
||
/// 3. 新对象原型取 target.prototype(new 表达式路径 newTarget 恒等于构造器,
|
||
/// 规范 SameValue 替换后 newTarget = target)
|
||
/// 4. 构造调用:native target 值传递(receiver = 新对象);字节码 target 压
|
||
/// 构造帧(this = 新对象,派生 target 为 undefined,new.target = target)
|
||
///
|
||
/// # 边界与前提
|
||
/// - `call_args` 为调用点实参(寄存器连续区或 spread 物化),绑定实参前置拼接
|
||
/// - 原型属性非对象时保持 object_proto 默认(OrdinaryCreateFromConstructor 回退)
|
||
pub(crate) fn dispatch_new_bound(
|
||
&mut self, rd: usize, wrapper_val: JsValue, new_obj: *mut JsObject, mut call_args: Vec<JsValue>,
|
||
call_window: u8,
|
||
) -> Result<bool, String> {
|
||
// 逐层解包 bound 链,绑定实参前置拼接。
|
||
let mut target_val = wrapper_val;
|
||
loop {
|
||
let wrapper_obj = unsafe { &*target_val.as_js_object_ptr() };
|
||
// 绑定状态对象 [target, thisArg, ...boundArgs](oxide_builtins::function
|
||
// 固定第 4 号 shape 属性):target 取存储槽 0,绑定实参取存储槽 2+。
|
||
let state = oxide_builtins::function::bound_state_values(wrapper_obj);
|
||
let target = state.first().copied().unwrap_or(JsValue::undefined());
|
||
let mut combined: Vec<JsValue> = state.iter().skip(2).copied().collect();
|
||
combined.extend_from_slice(&call_args);
|
||
call_args = combined;
|
||
let is_bound = target.is_object()
|
||
&& !target.as_js_object_ptr().is_null()
|
||
&& unsafe { &*target.as_js_object_ptr() }.type_tag == JsObject::OBJ_TYPE_BOUND;
|
||
target_val = target;
|
||
if !is_bound {
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 校验最内层 target 可构造。
|
||
if !target_val.is_object() || target_val.as_js_object_ptr().is_null() {
|
||
return self.raise_type_error("object is not a constructor").map(|_| true);
|
||
}
|
||
let target_obj = unsafe { &*target_val.as_js_object_ptr() };
|
||
if !target_obj.is_function() || target_obj.is_arrow() {
|
||
return self.raise_type_error("object is not a constructor").map(|_| true);
|
||
}
|
||
if target_obj.native_fn().is_some() && target_obj.type_tag != JsObject::OBJ_TYPE_CONSTRUCTOR {
|
||
return self.raise_type_error("object is not a constructor").map(|_| true);
|
||
}
|
||
// 按 target 自身记录的表代际解析:生成器/异步函数不是构造器。
|
||
if matches!(self.callee_module(target_obj), Some(sub) if sub.is_generator || sub.is_async) {
|
||
return self.raise_type_error("object is not a constructor").map(|_| true);
|
||
}
|
||
|
||
// 新对象原型取最内层 target 的 prototype(bound 包装自身无 prototype)。
|
||
// 读形与 NEW 读体同构:native target 裸读,字节码 target 传播读
|
||
// (getter 异常原值上抛,非对象回落 Object.prototype)。
|
||
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||
let proto_val = if target_obj.native_fn().is_some() {
|
||
self.resolve_property(target_obj, proto_si)
|
||
} else {
|
||
let pc_before = self.pc;
|
||
match self.ordinary_get(target_obj, proto_si, target_val) {
|
||
Ok(v) if self.pc == pc_before => Some(v),
|
||
Ok(_) => {
|
||
return Ok(true);
|
||
}
|
||
Err(err) => {
|
||
let exc = self
|
||
.last_uncaught_value
|
||
.take()
|
||
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &err));
|
||
self.exception_value = Some(exc);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
return self.unwind().map(|_| true);
|
||
}
|
||
}
|
||
};
|
||
if let Some(v) = proto_val {
|
||
if v.is_object() {
|
||
let new_obj_mut = unsafe { &mut *new_obj };
|
||
let proto_obj_ptr = v.as_js_object_ptr();
|
||
let _ = new_obj_mut.set_proto(JsValue::from_js_object(proto_obj_ptr));
|
||
}
|
||
}
|
||
let new_obj_val = JsValue::object(new_obj as *mut u8);
|
||
|
||
if target_obj.native_fn().is_some() {
|
||
// native 构造器:receiver = 新对象,值传递调用(错误原值恢复后展开)。
|
||
// new.target 按 bound [[Construct]] 语义取最外层 bound 包装(wrapper_val),
|
||
// 快照后置入 reg(255) 暴露给 native 构造器(调用后恢复原值);构造
|
||
// 形态标记同窗夹持(调用后恢复)。
|
||
let saved_new_target = self.regs[255];
|
||
let saved_constructing = self.constructing_native;
|
||
self.regs[255] = wrapper_val;
|
||
self.constructing_native = true;
|
||
let result = self.call_function_sync(target_val, new_obj_val, &call_args);
|
||
self.constructing_native = saved_constructing;
|
||
self.regs[255] = saved_new_target;
|
||
match result {
|
||
Ok(v) => {
|
||
self.regs[rd] = if v.is_object() { v } else { new_obj_val };
|
||
Ok(false)
|
||
}
|
||
Err(_) => {
|
||
let exc = self
|
||
.last_uncaught_value
|
||
.take()
|
||
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, "constructor call failed"));
|
||
self.exception_value = Some(exc);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
self.unwind().map(|_| true)
|
||
}
|
||
}
|
||
} else if target_obj.sub_module_index() > 0 {
|
||
// 字节码构造器:this = 新对象(派生 target 为 undefined,super() 装配),
|
||
// new.target = 最内层 target。
|
||
let this_value = if target_obj.is_derived_constructor() {
|
||
JsValue::undefined()
|
||
} else {
|
||
new_obj_val
|
||
};
|
||
self.push_bytecode_frame(
|
||
target_val,
|
||
this_value,
|
||
FrameArgs::Slice(&call_args),
|
||
ConstructArgs::new(rd as u8, new_obj_val, target_val),
|
||
FrameContinuation::None,
|
||
call_window,
|
||
)?;
|
||
Ok(true)
|
||
} else {
|
||
self.raise_type_error("object is not a constructor").map(|_| true)
|
||
}
|
||
}
|
||
|
||
pub(crate) fn dispatch_template_str(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("TEMPLATE_STR rd={}", rd);
|
||
let header = self.bytecode[self.pc];
|
||
self.pc += 1;
|
||
let segment_count = (header >> 16) as usize;
|
||
let len_hint = (header & 0xFFFF) as usize;
|
||
|
||
// 单元缓冲:字符串操作数按单元序列展开(lone surrogate 保真),
|
||
// len_hint 为单元数口径(emit 侧同口径)。
|
||
let mut units: Vec<u16> = Vec::with_capacity(len_hint.max(16));
|
||
for _ in 0..segment_count {
|
||
let seg = self.bytecode[self.pc];
|
||
self.pc += 1;
|
||
if (seg >> 31) == 1 {
|
||
let reg = (seg & 0x7FFF_FFFF) as usize;
|
||
let val = self.regs[reg];
|
||
if val.is_string() {
|
||
// SAFETY: val 是字符串值;借用仅在本次展开内消费,不跨分配点。
|
||
let s = unsafe { &*val.as_string_ptr() };
|
||
units.extend_from_slice(s.units().as_ref());
|
||
} else if val.is_object() || val.is_symbol() {
|
||
// 对象走完整 ToString(ToPrimitive 副作用顺序,结果保单元);
|
||
// Symbol 抛 TypeError(to_units_full 无 symbol 直写分支)。
|
||
units.extend(to_units_full(val, self)?);
|
||
} else {
|
||
// 原始值直写结果缓冲,免中间 String。
|
||
push_units_to(val, &mut units);
|
||
}
|
||
} else {
|
||
let const_idx = (seg & 0x7FFF_FFFF) as usize;
|
||
let imm = self.immutables();
|
||
if const_idx < imm.len() {
|
||
let val = imm[const_idx];
|
||
if val.is_string() {
|
||
// SAFETY: val 是字符串值;借用仅在本次展开内消费,不跨分配点。
|
||
let s = unsafe { &*val.as_string_ptr() };
|
||
units.extend_from_slice(s.units().as_ref());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
self.regs[rd] = self.new_string_units_owned(units);
|
||
Ok(())
|
||
}
|
||
|
||
pub(crate) fn dispatch_instanceof(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||
vm_trace!("INSTANCEOF rd={}", rd);
|
||
let lhs_val = self.regs[a];
|
||
let rhs_val = self.regs[b];
|
||
|
||
if !rhs_val.is_object() {
|
||
return self.raise_type_error("INSTANCEOF right-hand side is not callable");
|
||
}
|
||
|
||
let has_instance_si = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_HAS_INSTANCE);
|
||
|
||
let ctor_obj = unsafe { &*rhs_val.as_js_object_ptr() };
|
||
let has_instance_val = self.ordinary_get(ctor_obj, has_instance_si, rhs_val)?;
|
||
if !has_instance_val.is_undefined() && !has_instance_val.is_null() {
|
||
let fn_ptr = has_instance_val.as_js_object_ptr();
|
||
if !fn_ptr.is_null() {
|
||
let fn_obj = unsafe { &*fn_ptr };
|
||
if fn_obj.is_function() {
|
||
let result = self.call_function_sync(has_instance_val, rhs_val, &[lhs_val])?;
|
||
self.regs[rd] = JsValue::bool(to_boolean(result));
|
||
return Ok(());
|
||
}
|
||
}
|
||
}
|
||
|
||
if !lhs_val.is_object() {
|
||
self.regs[rd] = JsValue::bool(false);
|
||
return Ok(());
|
||
}
|
||
|
||
let rhs_obj = unsafe { &*rhs_val.as_js_object_ptr() };
|
||
let proto_si = self.kernel_core.perm_interner().intern("prototype").0;
|
||
let ctor_proto = self.resolve_property(rhs_obj, proto_si);
|
||
|
||
let ctor_proto_ptr = match ctor_proto {
|
||
Some(v) if v.is_object() => v.as_js_object_ptr(),
|
||
_ => {
|
||
self.regs[rd] = JsValue::bool(false);
|
||
return Ok(());
|
||
}
|
||
};
|
||
|
||
let mut proto = unsafe { &*lhs_val.as_js_object_ptr() }.proto();
|
||
let mut depth = 0usize;
|
||
loop {
|
||
if !proto.is_object() {
|
||
self.regs[rd] = JsValue::bool(false);
|
||
break;
|
||
}
|
||
if depth >= MAX_PROTO_CHAIN_DEPTH {
|
||
self.regs[rd] = JsValue::bool(false);
|
||
break;
|
||
}
|
||
depth += 1;
|
||
let proto_ptr = proto.as_js_object_ptr();
|
||
if proto_ptr == ctor_proto_ptr {
|
||
self.regs[rd] = JsValue::bool(true);
|
||
break;
|
||
}
|
||
proto = unsafe { &*proto_ptr }.proto();
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
pub(crate) fn dispatch_in(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||
vm_trace!("IN rd={}", rd);
|
||
let key_val = self.regs[a];
|
||
let obj_ptr = self.regs[b].as_object_ptr() as *mut JsObject;
|
||
if obj_ptr.is_null() {
|
||
return self.raise_type_error("IN right-hand side is not an object");
|
||
}
|
||
let obj = unsafe { &*obj_ptr };
|
||
let prop_name_si = self.property_key_si(key_val)?;
|
||
// `in` 即 HasProperty:原型链各层都要看数组元素区与 TypedArray 整数索引,
|
||
// 读值解析不覆盖这两处。deferred namespace 目标先触发求值(cyclic 抛错)。
|
||
let found = self.has_property(obj, prop_name_si)?;
|
||
self.regs[rd] = JsValue::bool(found);
|
||
Ok(())
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_in_init(&mut self, a: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_IN_INIT r{}={:?}", a, self.regs[a]);
|
||
let mut obj_val = self.regs[a];
|
||
if obj_val.is_null() || obj_val.is_undefined() {
|
||
// null/undefined 枚举不到任何键——是空 for-in,而非 TypeError。
|
||
let iter = Box::into_raw(Box::new(ForInIter { keys: Vec::new(), index: 0 }));
|
||
self.iters.push_for_in(iter);
|
||
return Ok(());
|
||
}
|
||
if !obj_val.is_object() {
|
||
// ToObject:字符串盒构造期已物化索引属性面,其余原始值盒无自身可枚举属性。
|
||
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;
|
||
|
||
// 数组把整型下标元素存在 prop_vec,而 prop_vec 不属于 shape 链。
|
||
// 单独枚举它们(ES:数组下标是可枚举字符串键,按升序排在其它自有键之前)。
|
||
// hole(删除标记)跳过——for-in 不枚举数组稀疏空洞。
|
||
if current.is_object() {
|
||
let arr = unsafe { &*current.as_js_object_ptr() };
|
||
if arr.is_array() {
|
||
for i in 0..arr.array_prop_count {
|
||
if arr.prop_meta_at(i).is_some_and(|m| m.is_hole()) {
|
||
continue;
|
||
}
|
||
let is_enum = arr
|
||
.prop_meta_at(i)
|
||
.map(|m| m.attributes.enumerable())
|
||
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
|
||
if is_enum {
|
||
// 整数下标编码为整数键(免 intern + 防永久泄漏);枚举值
|
||
// 物化为数字字符串,排序时由 array_index_from_property_key
|
||
// 直接反解。
|
||
let idx = make_int_key(i);
|
||
keys_vec.push((self.new_string(&i.to_string()), idx));
|
||
}
|
||
}
|
||
}
|
||
// TA 元素键:整数下标 0..live 长升序排前(尾稳定排序负责序);
|
||
// 越界(含 detach)live 长 0 零枚;元素住 buffer,不在形状链。
|
||
if arr.is_typed_array_obj() {
|
||
let len = oxide_builtins::typed_array::ta_view_length(self, arr) as u32;
|
||
for i in 0..len.min(INT_KEY_COUNT) {
|
||
keys_vec.push((self.new_string(&i.to_string()), make_int_key(i)));
|
||
}
|
||
}
|
||
}
|
||
|
||
let mut depth = 0usize;
|
||
|
||
loop {
|
||
if !current.is_object() {
|
||
break;
|
||
}
|
||
if depth >= MAX_PROTO_CHAIN_DEPTH {
|
||
break;
|
||
}
|
||
depth += 1;
|
||
let cur = unsafe { &*current.as_js_object_ptr() };
|
||
let obj_start = keys_vec.len();
|
||
let mut cursor = Some(cur.shape_id());
|
||
while let Some(id) = cursor {
|
||
if id == oxide_kernel::shape_forge::EMPTY_SHAPE_ID {
|
||
break;
|
||
}
|
||
if let Some(shape) = self.kernel_core.shape_forge().get_shape(id) {
|
||
// Symbol 键/私有名键不参与 for-in 枚举。
|
||
if shape.property_name != u32::MAX
|
||
&& !is_symbol_key(shape.property_name)
|
||
&& !is_private_name_key(shape.property_name)
|
||
&& seen.insert(shape.property_name)
|
||
{
|
||
let enumerable = self
|
||
.kernel_core
|
||
.shape_forge()
|
||
.lookup_position(cur.shape_id(), shape.property_name)
|
||
// 数组命名属性存储下标 = 元素区偏移加 shape 槽位;
|
||
// 普通对象槽位即下标,空数组偏移为零。
|
||
.and_then(|pos| {
|
||
cur.prop_meta_at(if cur.is_array() { cur.array_prop_count + pos } else { pos })
|
||
})
|
||
.map(|meta| meta.attributes.enumerable())
|
||
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
|
||
if enumerable {
|
||
// 整数键不在 interner:物化为数字字符串;其余键取永久串。
|
||
let key_val = if is_int_key(shape.property_name) {
|
||
self.new_string(&int_key_value(shape.property_name).to_string())
|
||
} else {
|
||
JsValue::perm_string(self.kernel_core.perm_interner().string_ptr(shape.property_name))
|
||
};
|
||
keys_vec.push((key_val, shape.property_name));
|
||
}
|
||
}
|
||
cursor = shape.parent;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
// shape 链按叶→根遍历(与插入序相反);在接上原型前把本对象的切片
|
||
// 翻转为插入序。
|
||
keys_vec[obj_start..].reverse();
|
||
current = cur.proto();
|
||
}
|
||
|
||
// 模块命名空间 exotic:EnumerateObjectProperties 构建键表时逐键走
|
||
// `? [[GetOwnProperty]]`,未初始化导出抛 ReferenceError。校验须在排序与
|
||
// 迭代器建立之前(INIT 期),不遗留 for-in 迭代器;非 live ns 无条目表,
|
||
// `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() {
|
||
for (_key_val, si) in &keys_vec {
|
||
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(target, *si)
|
||
{
|
||
return self
|
||
.raise_error_kind("ReferenceError", oxide_builtins::module::NS_UNINITIALIZED_MESSAGE);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ES 枚举序:整型下标键升序,其余按插入序。稳定排序保持非下标键间的插入序。
|
||
keys_vec.sort_by(|(_, a_si), (_, b_si)| {
|
||
match (self.array_index_from_property_key(*a_si), self.array_index_from_property_key(*b_si)) {
|
||
(Some(ai), Some(bi)) => ai.cmp(&bi),
|
||
(Some(_), None) => std::cmp::Ordering::Less,
|
||
(None, Some(_)) => std::cmp::Ordering::Greater,
|
||
(None, None) => std::cmp::Ordering::Equal,
|
||
}
|
||
});
|
||
|
||
// std Vec 直接作为迭代器体;体是堆上 Box,生命周期随迭代器表与
|
||
// 挂起状态盒,出表/状态盒释放时逐条释放。
|
||
let iter = Box::into_raw(Box::new(ForInIter { keys: keys_vec, index: 0 }));
|
||
self.iters.push_for_in(iter);
|
||
Ok(())
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_in_next(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_IN_NEXT rd={}", rd);
|
||
let iter_ptr = self.iters.last_for_in();
|
||
if iter_ptr.is_null() {
|
||
return Err("FOR_IN_NEXT without active iterator".into());
|
||
}
|
||
let iter = unsafe { &mut *iter_ptr };
|
||
if iter.index < iter.keys.len() {
|
||
self.regs[rd] = iter.keys[iter.index].0;
|
||
iter.index += 1;
|
||
} else {
|
||
self.regs[rd] = JsValue::undefined();
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_in_done(&mut self, rd: usize) {
|
||
vm_trace!("FOR_IN_DONE rd={}", rd);
|
||
let iter_ptr = self.iters.last_for_in();
|
||
if iter_ptr.is_null() {
|
||
self.regs[rd] = JsValue::bool(true);
|
||
} else {
|
||
let iter = unsafe { &*iter_ptr };
|
||
self.regs[rd] = JsValue::bool(iter.index >= iter.keys.len());
|
||
}
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_in_cleanup(&mut self) {
|
||
vm_trace!("FOR_IN_CLEANUP");
|
||
self.iters.pop_for_in();
|
||
}
|
||
|
||
/// using/await using 声明登记:把资源值(rd 槽)与释放提示(a 槽)压入释放栈。
|
||
///
|
||
/// 释放提示 0 = 同步、1 = 异步,登记期原样保留供释放点分派;
|
||
/// 本指令只登记,释放时机由作用域出口点决定。
|
||
///
|
||
/// # 副作用
|
||
/// - 释放栈增长(栈内 JsValue 是 GC 根,run 边界清空)。
|
||
pub(crate) fn dispatch_dispose_register(&mut self, rd: usize, a: usize) {
|
||
vm_trace!("DISPOSE_REGISTER rd={} hint={}", rd, a);
|
||
self.dispose_stack.push((self.regs[rd], a as u16));
|
||
}
|
||
|
||
/// 作用域入口水位:把当前释放栈深度压入水位栈(DISPOSE_POP 的配对入口)。
|
||
///
|
||
/// # 副作用
|
||
/// - 水位栈增长。
|
||
pub(crate) fn dispatch_dispose_mark(&mut self) {
|
||
vm_trace!("DISPOSE_MARK depth={}", self.dispose_stack.len());
|
||
self.dispose_marks.push(self.dispose_stack.len());
|
||
}
|
||
|
||
/// 作用域出口:弹出水位,逆序释放水位以上资源并截断释放栈(规范
|
||
/// DisposeResources)。多个释放方法抛错时按规范折叠成 SuppressedError 链
|
||
/// (后抛为 error、前值为 suppressed),循环不因错中断;收尾有合并错误时
|
||
/// 写入在途异常并展开到最近处理器。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 正常完成穿越:completion 初值为 None(无在途异常)。
|
||
///
|
||
/// # 副作用
|
||
/// - 释放栈截断到水位、水位栈弹出;释放方法是任意 JS 调用(可再入,
|
||
/// 嵌套 using 声明会在其内再压释放栈)。
|
||
/// - 有合并错误时改写 `exception_value`/`pending_error_kind` 并触发 `unwind`。
|
||
pub(crate) fn dispatch_dispose_pop(&mut self) -> Result<(), String> {
|
||
// 重入守卫:释放方法抛错触发的嵌套 unwind 会重入本函数,置位时直接返回,
|
||
// 防重复释放与合并(先于弹水位,防重入误弹外层水位)。
|
||
if self.disposing {
|
||
return Ok(());
|
||
}
|
||
let mark = match self.dispose_marks.pop() {
|
||
Some(m) => m,
|
||
None => return Ok(()),
|
||
};
|
||
vm_trace!("DISPOSE_POP mark={} depth={}", mark, self.dispose_stack.len());
|
||
self.disposing = true;
|
||
let mut completion: Option<JsValue> = None;
|
||
while self.dispose_stack.len() > mark {
|
||
let (value, hint) = self.dispose_stack.pop().expect("水位以上必有条目");
|
||
match self.dispose_value(value, hint)? {
|
||
DisposeOutcome::Done | DisposeOutcome::NoMethod => {}
|
||
DisposeOutcome::Err(exc) => {
|
||
completion = oxide_builtins::disposable_stack::merge_dispose_error(self, completion, exc);
|
||
}
|
||
DisposeOutcome::Suspended(promise) => {
|
||
// 异步挂起:登记在途状态(水位已在循环前弹出,恢复后续循环不
|
||
// 重复弹),置挂起信号让派发循环退出由恢复方快照。
|
||
self.pending_async_dispose = Some(PendingAsyncDispose {
|
||
dispose_promise: promise,
|
||
mark,
|
||
completion,
|
||
kind: DisposeResumeKind::Pop,
|
||
});
|
||
self.disposing = false;
|
||
return Ok(());
|
||
}
|
||
}
|
||
}
|
||
self.disposing = false;
|
||
if let Some(exc) = completion {
|
||
self.exception_value = Some(exc);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
self.unwind()?;
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// 批量释放核心:逆序释放 `depth` 以上条目并截断释放栈到 `depth`,同步弹出
|
||
/// 水位栈中 `>= depth` 的基线条目(保两栈一致,防异常路径基线泄漏)。
|
||
///
|
||
/// # 步骤
|
||
/// 1. 在途异常作 completion 种子(`seed`,异常穿越时被折叠进 suppressed)。
|
||
/// 2. 逐条弹出并释放(`dispose_value`);多个释放方法抛错时按规范折叠成
|
||
/// SuppressedError 链(后抛为 error、前值为 suppressed),循环不因错中断。
|
||
/// 3. 异步挂起时登记 `PendingAsyncDispose`(携给定 `kind` 与在途合并错误)并
|
||
/// 返回 `Suspended`。
|
||
/// 4. 收尾写回 completion:有合并错误则替代在途异常并更新错误类型;无合并
|
||
/// 错误则还原在途异常(类型不变)。
|
||
/// 5. 截断释放栈后,弹出水位栈中 `>= depth` 的基线条目。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 重入守卫:释放方法抛错触发的嵌套 unwind 会重入本函数,`disposing` 置位时
|
||
/// 直接返回(防重复释放与合并,合并链会被二次折叠)。
|
||
/// - 挂起路径基线水位暂不弹(恢复续循环后统一弹)。
|
||
pub(crate) fn dispose_above_core(
|
||
&mut self, depth: usize, kind: DisposeResumeKind, seed: Option<JsValue>,
|
||
) -> Result<DisposePhase, String> {
|
||
if self.disposing {
|
||
return Ok(DisposePhase::Clean);
|
||
}
|
||
self.disposing = true;
|
||
let mut completion: Option<JsValue> = seed;
|
||
let mut threw = false;
|
||
let truncated = self.dispose_stack.len() > depth;
|
||
while self.dispose_stack.len() > depth {
|
||
let (value, hint) = self.dispose_stack.pop().expect("水位以上必有条目");
|
||
match self.dispose_value(value, hint)? {
|
||
DisposeOutcome::Done | DisposeOutcome::NoMethod => {}
|
||
DisposeOutcome::Err(exc) => {
|
||
completion = oxide_builtins::disposable_stack::merge_dispose_error(self, completion, exc);
|
||
threw = true;
|
||
}
|
||
DisposeOutcome::Suspended(promise) => {
|
||
// 异步挂起:登记在途状态,基线水位暂不弹(恢复续循环后统一弹)。
|
||
self.pending_async_dispose = Some(PendingAsyncDispose {
|
||
dispose_promise: promise,
|
||
mark: depth,
|
||
completion,
|
||
kind,
|
||
});
|
||
self.disposing = false;
|
||
return Ok(DisposePhase::Suspended);
|
||
}
|
||
}
|
||
}
|
||
if let Some(exc) = completion {
|
||
self.exception_value = Some(exc);
|
||
if threw {
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
}
|
||
}
|
||
// 仅在实际截断释放栈时同步弹基线,防 no-op 调用(如 dispose_top_n(0))
|
||
// 误弹外层作用域的水位。
|
||
if truncated {
|
||
while self.dispose_marks.last().is_some_and(|&m| m >= depth) {
|
||
self.dispose_marks.pop();
|
||
}
|
||
}
|
||
self.disposing = false;
|
||
Ok(if threw { DisposePhase::Threw } else { DisposePhase::Clean })
|
||
}
|
||
|
||
/// 异常穿越释放:逆序释放 `depth` 以上资源。在途异常作 completion 种子(被折叠
|
||
/// 进 suppressed)。挂起时携 `Unwind(handler)`(handler 已弹出,恢复时重压再入
|
||
/// unwind)。
|
||
pub(crate) fn dispose_above(&mut self, depth: usize, handler: Option<TryHandler>) -> Result<DisposePhase, String> {
|
||
self.dispose_above_core(depth, DisposeResumeKind::Unwind(handler), self.exception_value)
|
||
}
|
||
|
||
/// 释放栈顶部 `n` 条资源(逃出释放:return/break/continue 完成值逃出作用域时
|
||
/// 逆序释放被逃出的资源)。`n` 超过栈长时按全量释放。在途 completion 初值为
|
||
/// None(完成值非 throw),挂起时携 `Escape(completion)` 供恢复后执行。
|
||
pub(crate) fn dispose_top_n(&mut self, n: usize, completion: Completion) -> Result<DisposePhase, String> {
|
||
let depth = self.dispose_stack.len().saturating_sub(n);
|
||
self.dispose_above_core(depth, DisposeResumeKind::Escape(completion), None)
|
||
}
|
||
|
||
/// 单资源释放(规范 Dispose 语义):按释放提示取方法(同步只取 Symbol.dispose;
|
||
/// 异步先取 Symbol.asyncDispose,未定义回退 Symbol.dispose),以资源自身为 this
|
||
/// 零参调用。
|
||
///
|
||
/// # 返回值
|
||
/// `Done` = 释放成功;`NoMethod` = 无释放方法(跳过);`Err(v)` = 释放方法抛错
|
||
/// (v 为原始异常值,经 `last_uncaught_value` 恢复,无则按错误文本建普通 Error);
|
||
/// `Suspended(promise)` = 异步释放已挂起(promise 为释放方法结果经 PromiseResolve
|
||
/// 包装后登记的结算 promise,调用方据此登记 `PendingAsyncDispose`)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 取方法 / 调用方法任一步抛错均归入 `Err`(getter 抛错与调用抛错同处置)。
|
||
/// - 异步提示恒挂起:方法未定义时结果取 undefined,仍经 PromiseResolve 包成新
|
||
/// promise 让出一次微任务(规范 `await using` 的 Await 步无条件执行)。
|
||
fn dispose_value(&mut self, value: JsValue, hint: u16) -> Result<DisposeOutcome, String> {
|
||
// 取方法抛错(getter 抛错)归入 Err 结局(合并进链)。
|
||
let method = match self.lookup_dispose_method(value, hint) {
|
||
Ok(m) => m,
|
||
Err(exc) => return Ok(DisposeOutcome::Err(exc)),
|
||
};
|
||
// 异步提示且有异步上下文:调用方法(未定义时结果取 undefined),结果无条件
|
||
// Await 挂起。无异步上下文(顶层 await using)回退同步释放。
|
||
if hint == 1 && (self.async_context.is_some() || self.async_gen_context.is_some()) {
|
||
let result = if is_callable(method) {
|
||
match self.call_function_sync(method, value, &[]) {
|
||
Ok(v) => v,
|
||
Err(e) => return Ok(DisposeOutcome::Err(self.dispose_exc(e))),
|
||
}
|
||
} else {
|
||
JsValue::undefined()
|
||
};
|
||
let promise = match self.promise_resolve(result) {
|
||
Ok(p) => p,
|
||
Err(exc) => {
|
||
let (p, _, _) = self.new_promise_capability();
|
||
let _ = self.reject_promise(p, exc);
|
||
p
|
||
}
|
||
};
|
||
self.register_dispose_suspend(promise)?;
|
||
return Ok(DisposeOutcome::Suspended(promise));
|
||
}
|
||
// 同步(或无异步上下文的异步提示):不可调用跳过;调用成功 Done,抛错 Err。
|
||
if !is_callable(method) {
|
||
return Ok(DisposeOutcome::NoMethod);
|
||
}
|
||
match self.call_function_sync(method, value, &[]) {
|
||
Ok(_) => Ok(DisposeOutcome::Done),
|
||
Err(e) => Ok(DisposeOutcome::Err(self.dispose_exc(e))),
|
||
}
|
||
}
|
||
|
||
/// 取释放方法:异步提示先取 Symbol.asyncDispose,未定义回退 Symbol.dispose;
|
||
/// 同步提示只取 Symbol.dispose。非对象取 undefined。getter 抛错时返回原始
|
||
/// 异常值(经 `last_uncaught_value` 恢复,无则按错误文本建普通 Error)。
|
||
fn lookup_dispose_method(&mut self, value: JsValue, hint: u16) -> Result<JsValue, JsValue> {
|
||
if !value.is_object() {
|
||
return Ok(JsValue::undefined());
|
||
}
|
||
let val_obj = unsafe { &*value.as_js_object_ptr() };
|
||
let dispose_key = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_DISPOSE);
|
||
let method = if hint == 1 {
|
||
let async_key = encode_symbol_key(self.realm_id(), WELL_KNOWN_SYMBOL_ASYNC_DISPOSE);
|
||
let async_method = self.ordinary_get(val_obj, async_key, value).map_err(|e| self.dispose_exc(e))?;
|
||
if async_method.is_undefined() {
|
||
self.ordinary_get(val_obj, dispose_key, value)
|
||
.map_err(|e| self.dispose_exc(e))?
|
||
} else {
|
||
async_method
|
||
}
|
||
} else {
|
||
self.ordinary_get(val_obj, dispose_key, value)
|
||
.map_err(|e| self.dispose_exc(e))?
|
||
};
|
||
Ok(method)
|
||
}
|
||
|
||
/// 登记异步释放挂起:按派发上下文(异步生成器 / 普通异步函数)构造结算闭包,
|
||
/// 登记 fulfill/reject 恢复反应并置挂起信号。
|
||
///
|
||
/// # 边界与前提
|
||
/// - 异步生成器派发(`async_gen_dispatch`)走异步生成器状态盒与结算闭包,置
|
||
/// `async_gen_suspended`;普通异步函数走异步上下文,置 `async_suspended`。
|
||
/// - 无对应上下文(非异步上下文内的异步释放)返回 Err,不静默降级。
|
||
fn register_dispose_suspend(&mut self, promise: JsValue) -> Result<(), String> {
|
||
if self.async_gen_dispatch {
|
||
let Some(ctx) = self.async_gen_context else {
|
||
return Err("async dispose in async generator without context".into());
|
||
};
|
||
let fulfill_fn = self.make_async_gen_dispose_resume_fn(ctx, promise, false);
|
||
let reject_fn = self.make_async_gen_dispose_resume_fn(ctx, promise, true);
|
||
let _ = self.perform_promise_then(promise, fulfill_fn, reject_fn);
|
||
self.async_gen_suspended = true;
|
||
return Ok(());
|
||
}
|
||
let Some(ctx) = self.async_context else {
|
||
return Err("async dispose outside async function".into());
|
||
};
|
||
let fulfill_fn = self.make_async_dispose_resume_fn(ctx, promise, false);
|
||
let reject_fn = self.make_async_dispose_resume_fn(ctx, promise, true);
|
||
let _ = self.perform_promise_then(promise, fulfill_fn, reject_fn);
|
||
self.async_suspended = true;
|
||
Ok(())
|
||
}
|
||
|
||
/// 释放路径抛错时恢复原始异常值(经 `last_uncaught_value`,无则按错误文本建
|
||
/// 普通 Error)。
|
||
fn dispose_exc(&mut self, e: String) -> JsValue {
|
||
self.last_uncaught_value
|
||
.take()
|
||
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, &e))
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_of_init(&mut self, a: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_OF_INIT r{}={:?}", a, self.regs[a]);
|
||
let iterable = self.regs[a];
|
||
match oxide_builtins::iterator::make_iterator_for_value(self, iterable) {
|
||
Ok(iterator) => {
|
||
// 内置包装器检测:Array/String/TA/Map/Set 走快路径(异步恒慢)。
|
||
// init 期把 inner 快照入条目,步内不重读 `__inner__` 槽。
|
||
let (kind, inner) = match oxide_builtins::iterator::builtin_iter_kind(self, iterator) {
|
||
Some((k, i)) => (Some(k), i),
|
||
None => (None, JsValue::undefined()),
|
||
};
|
||
self.iters.push_for_of(iterator, false, kind, inner);
|
||
Ok(())
|
||
}
|
||
Err(err) => {
|
||
self.exception_value = Some(err);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(err));
|
||
self.unwind()
|
||
}
|
||
}
|
||
}
|
||
|
||
/// for-await-of 初始化:按 GetAsyncIterator 协议取异步迭代器(缺失 `@@asyncIterator`
|
||
/// 时回退同步迭代器并包 AsyncFromSyncIterator),压入 for-of 迭代器栈。
|
||
pub(crate) fn dispatch_for_await_of_init(&mut self, a: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_AWAIT_OF_INIT r{}={:?}", a, self.regs[a]);
|
||
let iterable = self.regs[a];
|
||
match crate::async_from_sync::make_async_iterator(self, iterable) {
|
||
Ok(iterator) => {
|
||
// for-await-of 恒慢路径:无快路径快照,inner 传 undefined。
|
||
self.iters.push_for_of(iterator, true, None, JsValue::undefined());
|
||
Ok(())
|
||
}
|
||
Err(err) => {
|
||
self.exception_value = Some(err);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(err));
|
||
self.unwind()
|
||
}
|
||
}
|
||
}
|
||
|
||
/// for-await-of 步进:调用迭代器 `next()`,把返回的 promise 写入 rd。
|
||
/// 随后的 `AWAIT` 负责挂起等待;next() 抛出时弹出迭代器并透传(与同步 for-of 一致)。
|
||
pub(crate) fn dispatch_for_await_of_next(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_AWAIT_OF_NEXT rd={}", rd);
|
||
self.last_uncaught_value = None;
|
||
let Some(iterator) = self.iters.last_for_of() else {
|
||
return Err("FOR_AWAIT_OF_NEXT without active iterator".into());
|
||
};
|
||
if !iterator.is_object() {
|
||
return Err("FOR_AWAIT_OF_NEXT iterator is not an object".into());
|
||
}
|
||
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
|
||
let next_si = self.kernel_core.perm_interner().intern("next").0;
|
||
let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) {
|
||
Ok(v) => v,
|
||
Err(e) => return self.throw_for_of_error(e),
|
||
};
|
||
let result = match self.call_function_sync(next_fn, iterator, &[]) {
|
||
Ok(v) => v,
|
||
Err(e) => return self.throw_for_of_error(e),
|
||
};
|
||
self.regs[rd] = result;
|
||
Ok(())
|
||
}
|
||
|
||
/// for-await-of 步进完成检查:读取 `AWAIT` 恢复值(a 槽,即迭代器结果对象)的
|
||
/// `done`,写入本迭代器条目结果并把 `!done` 写 rd(供 JMP_IF_FALSE 分支)。
|
||
pub(crate) fn dispatch_for_await_of_done(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_AWAIT_OF_DONE rd={} r{}={:?}", rd, a, self.regs[a]);
|
||
let result = self.regs[a];
|
||
if !result.is_object() {
|
||
return self.raise_type_error("iterator result is not an object");
|
||
}
|
||
self.iters.set_last_result(result);
|
||
let result_obj = unsafe { &*result.as_js_object_ptr() };
|
||
let done_si = self.kernel_core.perm_interner().intern("done").0;
|
||
let done_val = match self.ordinary_get(result_obj, done_si, result) {
|
||
Ok(v) => v,
|
||
Err(e) => return self.throw_for_of_error(e),
|
||
};
|
||
self.regs[rd] = JsValue::bool(!to_boolean(done_val));
|
||
Ok(())
|
||
}
|
||
|
||
/// for-await-of 收尾:迭代器自然 done 时跳过;否则执行异步 IteratorClose——
|
||
/// 调用 `return()`,其返回的 promise 经 await 挂起,恢复后继续循环后的指令。
|
||
pub(crate) fn dispatch_for_await_of_close(&mut self) -> Result<(), String> {
|
||
vm_trace!("FOR_AWAIT_OF_CLOSE");
|
||
let Some(entry) = self.iters.pop_for_of() else {
|
||
return Ok(());
|
||
};
|
||
let iterator = entry.iterator;
|
||
let result = entry.last_result;
|
||
if result.is_object() {
|
||
let result_obj = unsafe { &*result.as_js_object_ptr() };
|
||
let done_si = self.kernel_core.perm_interner().intern("done").0;
|
||
if let Ok(done_val) = self.ordinary_get(result_obj, done_si, result) {
|
||
if to_boolean(done_val) {
|
||
return Ok(());
|
||
}
|
||
}
|
||
}
|
||
if !iterator.is_object() {
|
||
return Ok(());
|
||
}
|
||
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
|
||
let return_si = self.kernel_core.perm_interner().intern("return").0;
|
||
let return_fn = match self.ordinary_get(iter_obj, return_si, iterator) {
|
||
Ok(v) => v,
|
||
// return getter 抛出:恢复原始异常值并就地展开,使外围 try/catch 可捕获。
|
||
Err(e) => return self.throw_for_of_close_error(&e),
|
||
};
|
||
if !return_fn.is_object() {
|
||
return Ok(());
|
||
}
|
||
if !unsafe { &*return_fn.as_js_object_ptr() }.is_function() {
|
||
return Ok(());
|
||
}
|
||
let inner = match self.call_function_sync(return_fn, iterator, &[]) {
|
||
Ok(v) => v,
|
||
// return() 抛出:恢复原始异常值并就地展开,使外围 try/catch 可捕获。
|
||
Err(e) => return self.throw_for_of_close_error(&e),
|
||
};
|
||
if !inner.is_object() {
|
||
return self.raise_type_error("iterator return() result is not an object");
|
||
}
|
||
self.perform_await(inner)
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_of_done(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_OF_DONE rd={}", rd);
|
||
let Some(iterator) = self.iters.last_for_of() else {
|
||
return Err("FOR_OF_DONE without active iterator".into());
|
||
};
|
||
if !iterator.is_object() {
|
||
return Err("FOR_OF_DONE iterator is not an object".into());
|
||
}
|
||
|
||
// 调用 next()/读 done 前清空异常值槽:跨调用残留不得污染本指令的取槽。
|
||
self.last_uncaught_value = None;
|
||
|
||
// 快路径:内置包装器(Array/String/TA/Map/Set)直步,跳过 native 调用帧、
|
||
// 结果对象分配与 done/value 两次读;inner 与游标取条目快照,步内不重跑槽协议。
|
||
if self.iters.for_of_iters.last().is_some_and(|e| e.fast.is_some()) {
|
||
// inner 与游标为 Copy 值:先拷出释放借用,再做步进核心的可变调用。
|
||
let (inner, cursor) = {
|
||
let e = self.iters.for_of_iters.last().unwrap();
|
||
(e.fast_inner, e.fast_cursor)
|
||
};
|
||
let stack_len = self.iters.for_of_iters.len();
|
||
let step = oxide_builtins::iterator::builtin_iter_fast_step(self, inner, cursor);
|
||
// 元素 getter / 访问器抛出:深度 0 的 raise_call_error 已就地 unwind
|
||
// (跳转 catch),条目已弹出、异常值已入异常通道——直接返回,派发循环
|
||
// 自 catch 点续行,不得再触碰迭代器栈。
|
||
// 判据须含栈深度:同迭代器两层嵌套时栈顶被外层条目顶替,仅凭 iterator
|
||
// 同一性会误判通过,须以步前深度不变作锚。
|
||
if self.iters.for_of_iters.len() != stack_len
|
||
|| !self.iters.for_of_iters.last().is_some_and(|e| e.iterator == iterator)
|
||
{
|
||
return Ok(());
|
||
}
|
||
match step {
|
||
Ok((value, done, next_cursor)) => {
|
||
let entry = self.iters.for_of_iters.last_mut().unwrap();
|
||
entry.fast_value = value;
|
||
entry.fast_cursor = next_cursor;
|
||
self.regs[rd] = JsValue::bool(!done);
|
||
}
|
||
Err(exc) => return self.throw_for_of_error_value(exc),
|
||
}
|
||
return Ok(());
|
||
}
|
||
|
||
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
|
||
let next_si = self.kernel_core.perm_interner().intern("next").0;
|
||
let next_fn = match self.ordinary_get(iter_obj, next_si, iterator) {
|
||
Ok(v) => v,
|
||
// 错误路径只经 throw_for_of_error 传播,不写 rd(rd 保持循环决策位,
|
||
// 异常展开后指令流离开循环,残留值无效)。
|
||
Err(e) => return self.throw_for_of_error(e),
|
||
};
|
||
let result = match self.call_function_sync(next_fn, iterator, &[]) {
|
||
Ok(v) => v,
|
||
Err(e) => return self.throw_for_of_error(e),
|
||
};
|
||
if !result.is_object() {
|
||
return self.raise_type_error("iterator result is not an object");
|
||
}
|
||
let result_obj = unsafe { &*result.as_js_object_ptr() };
|
||
let done_si = self.kernel_core.perm_interner().intern("done").0;
|
||
let done_val = match self.ordinary_get(result_obj, done_si, result) {
|
||
Ok(v) => v,
|
||
Err(e) => return self.throw_for_of_error(e),
|
||
};
|
||
let done = to_boolean(done_val);
|
||
self.iters.set_last_result(result);
|
||
self.regs[rd] = JsValue::bool(!done);
|
||
Ok(())
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_of_next(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("FOR_OF_NEXT rd={}", rd);
|
||
self.last_uncaught_value = None;
|
||
// 快路径:元素值由前一次 DONE 快步产出,直写寄存器。
|
||
if self.iters.for_of_iters.last().is_some_and(|e| e.fast.is_some()) {
|
||
self.regs[rd] = self.iters.for_of_iters.last().unwrap().fast_value;
|
||
return Ok(());
|
||
}
|
||
let result = self.iters.last_result();
|
||
if !result.is_object() {
|
||
self.regs[rd] = JsValue::undefined();
|
||
return Ok(());
|
||
}
|
||
let result_obj = unsafe { &*result.as_js_object_ptr() };
|
||
let value_si = self.kernel_core.perm_interner().intern("value").0;
|
||
self.regs[rd] = match self.ordinary_get(result_obj, value_si, result) {
|
||
Ok(v) => v,
|
||
// 错误路径只经 throw_for_of_error 传播,不写 rd(同上)。
|
||
Err(e) => return self.throw_for_of_error(e),
|
||
};
|
||
Ok(())
|
||
}
|
||
|
||
/// next()/value 访问抛出:经 unwind 走异常展开,使外围 try/catch 能捕获,并尽可能
|
||
/// 重新抛出原始值。按 ECMA-262,next() 抛出时不会经 return() 关闭迭代器——
|
||
/// 先弹出它,使展开时的 IteratorClose 遍历跳过该迭代器。
|
||
fn throw_for_of_error(&mut self, msg: String) -> Result<(), String> {
|
||
let exc = match self.last_uncaught_value.take() {
|
||
Some(v) => v,
|
||
None => oxide_builtins::error::create_from_text(self, &msg),
|
||
};
|
||
self.throw_for_of_error_value(exc)
|
||
}
|
||
|
||
/// 以指定异常值展开(慢路径文本重建与快路径原值透传共用)。
|
||
fn throw_for_of_error_value(&mut self, exc: JsValue) -> Result<(), String> {
|
||
self.iters.pop_for_of();
|
||
self.exception_value = Some(exc);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
// 无论取到原值还是重建错误对象,异常必须展开传播,不得静默返回 Ok——
|
||
// 否则 for-of 循环继续推进形成不终止。
|
||
self.unwind()
|
||
}
|
||
|
||
/// for-await-of 收尾抛错(return getter 或 return() 调用):恢复原始异常值并就地
|
||
/// 展开,使外围 try/catch 可捕获。本函数入口已弹出本迭代器条目,故展开路径不得
|
||
/// 再弹 for-of 栈(与 throw_for_of_error_value 的唯一区别)。
|
||
///
|
||
/// 直接 unwind 而非经 raise_call_error:后者以 native_call_depth 门控展开,
|
||
/// 异步函数恢复期深度恒大于零会误判为不展开,使异常绕过循环内 catch 直接
|
||
/// 拒绝 capability promise。
|
||
fn throw_for_of_close_error(&mut self, msg: &str) -> Result<(), String> {
|
||
let exc = self
|
||
.last_uncaught_value
|
||
.take()
|
||
.unwrap_or_else(|| oxide_builtins::error::create_from_text(self, msg));
|
||
self.exception_value = Some(exc);
|
||
self.pending_error_kind = Some(self.thrown_error_kind(exc));
|
||
self.unwind()
|
||
}
|
||
|
||
pub(crate) fn dispatch_for_of_close(&mut self) -> Result<(), String> {
|
||
vm_trace!("FOR_OF_CLOSE");
|
||
let Some(entry) = self.iters.pop_for_of() else {
|
||
return Ok(());
|
||
};
|
||
let iterator = entry.iterator;
|
||
let result = entry.last_result;
|
||
// 迭代已自然结束(最后一次 next 返回 done:true)时不调 return();
|
||
// 仅当元素耗尽但迭代器未 done(提前退出)才执行 IteratorClose。
|
||
if result.is_object() {
|
||
let result_obj = unsafe { &*result.as_js_object_ptr() };
|
||
let done_si = self.kernel_core.perm_interner().intern("done").0;
|
||
if let Ok(done_val) = self.ordinary_get(result_obj, done_si, result) {
|
||
if to_boolean(done_val) {
|
||
return Ok(());
|
||
}
|
||
}
|
||
}
|
||
// 正常 / break / return 退出:此前无进行中的突然完成,return() 自身的抛出直接传播。
|
||
self.close_for_of_iterator(iterator, false)
|
||
}
|
||
|
||
/// 调用 `iterator.return()`(IteratorClose)。
|
||
///
|
||
/// # 边界与前提
|
||
/// - `suppress_return_error` 为 true 时表示正在展开某个外围突然完成:return() 的
|
||
/// 自身结果被丢弃,并保留在途异常跨调用存活。
|
||
/// - 为 false 时(正常 for-of 退出)return() 的错误向外传播。
|
||
pub(crate) fn close_for_of_iterator(
|
||
&mut self, iterator: JsValue, suppress_return_error: bool,
|
||
) -> Result<(), String> {
|
||
if !iterator.is_object() {
|
||
return Ok(());
|
||
}
|
||
let iter_obj = unsafe { &*iterator.as_js_object_ptr() };
|
||
let return_si = self.kernel_core.perm_interner().intern("return").0;
|
||
let return_fn = match self.ordinary_get(iter_obj, return_si, iterator) {
|
||
Ok(v) => v,
|
||
Err(e) => {
|
||
if suppress_return_error {
|
||
return Ok(());
|
||
}
|
||
return Err(e);
|
||
}
|
||
};
|
||
if return_fn.is_object() {
|
||
let return_obj = unsafe { &*return_fn.as_js_object_ptr() };
|
||
if return_obj.is_function() {
|
||
if suppress_return_error {
|
||
let saved_exc = self.exception_value;
|
||
let saved_kind = self.pending_error_kind;
|
||
// 忽略调用期间的原始异常值一并暂存:return() 自身抛错按契约被在途
|
||
// 异常替代,其值不得外泄进槽供后续 take 误取。
|
||
let saved_uncaught = self.last_uncaught_value.take();
|
||
let _ = self.call_function_sync(return_fn, iterator, &[]);
|
||
self.exception_value = saved_exc;
|
||
self.pending_error_kind = saved_kind;
|
||
self.last_uncaught_value = saved_uncaught;
|
||
} else {
|
||
let inner = match self.call_function_sync(return_fn, iterator, &[]) {
|
||
Ok(v) => v,
|
||
// return() 抛出:恢复原始异常值并展开,使外围 try/catch 可捕获。
|
||
Err(e) => return self.raise_call_error(&e).map(|_| ()),
|
||
};
|
||
// IteratorClose 要求 return() 返回值是对象,否则抛 TypeError。
|
||
if !inner.is_object() {
|
||
self.raise_type_error("iterator return() result is not an object")?;
|
||
return Ok(());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// 对 `depth` 以上的所有活跃 for-of 迭代器执行 IteratorClose,供 `unwind()` 关闭
|
||
/// 被异常中断的循环。先弹出再关闭,防止重入调用重复关闭,同时跳过 next()-throw
|
||
/// 路径(它已弹出自己的迭代器)。
|
||
pub(crate) fn close_for_of_above(&mut self, depth: usize) {
|
||
while self.iters.for_of_iters.len() > depth {
|
||
let Some(entry) = self.iters.pop_for_of() else {
|
||
break;
|
||
};
|
||
let _ = self.close_for_of_iterator(entry.iterator, true);
|
||
}
|
||
}
|
||
|
||
pub(crate) fn dispatch_rest_object(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||
vm_trace!("REST_OBJECT rd={}", rd);
|
||
// ext 字(excluded 常量下标)在任何路径都先消费,防止 ToObject 早返回后
|
||
// pc 错位把 ext 字当下一条指令解码(曾误读成 UNSPILL slot)。
|
||
let excluded_idx = self.bytecode[self.pc] as usize;
|
||
self.pc += 1;
|
||
let src = self.regs[a];
|
||
if !src.is_object() {
|
||
// ToObject:null/undefined 抛;number/boolean/symbol 无自有属性 → 空对象;
|
||
// string 的索引字符(UTF-16 code unit)是可枚举自有属性。
|
||
if src.is_null() || src.is_undefined() {
|
||
return self.raise_type_error("Cannot destructure property of null/undefined");
|
||
}
|
||
let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||
let rest_ptr = self.alloc_object(JsObject::new_empty(
|
||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||
JsValue::from_js_object(proto_ptr),
|
||
));
|
||
if src.is_string() {
|
||
// SAFETY: src 是字符串值;单元序列展开(lone surrogate 保真)。
|
||
let code_units: Vec<u16> = unsafe { (*src.as_string_ptr()).units().into_owned() };
|
||
for (i, unit) in code_units.iter().enumerate() {
|
||
let si = make_int_key(i as u32);
|
||
let ch_val = self.unit_char_value(*unit);
|
||
let rest = unsafe { &mut *rest_ptr };
|
||
self.set_or_create_prop_value(rest, si, ch_val);
|
||
}
|
||
}
|
||
self.regs[rd] = JsValue::from_js_object(rest_ptr);
|
||
return Ok(());
|
||
}
|
||
// 排除名单常量按单元序列展开(lone surrogate 名保真),0x0000 分隔。
|
||
let excluded_const = self
|
||
.immutables()
|
||
.get(excluded_idx)
|
||
.and_then(|v| {
|
||
if v.is_string() {
|
||
// SAFETY: v 是字符串常量值。
|
||
Some(unsafe { (*v.as_string_ptr()).units().into_owned() })
|
||
} else {
|
||
None
|
||
}
|
||
})
|
||
.unwrap_or_default();
|
||
let mut excluded: std::collections::HashSet<u32> = std::collections::HashSet::new();
|
||
let mut seg: Vec<u16> = Vec::new();
|
||
for &u in excluded_const.iter() {
|
||
if u == 0x0000 {
|
||
if !seg.is_empty() {
|
||
excluded.insert(self.string_key_units(&seg));
|
||
seg.clear();
|
||
}
|
||
continue;
|
||
}
|
||
seg.push(u);
|
||
}
|
||
if !seg.is_empty() {
|
||
excluded.insert(self.string_key_units(&seg));
|
||
}
|
||
// 运行时 excluded:b 槽数组(computed key 求值结果)的元素 ToPropertyKey 后排除。
|
||
if b != 0 {
|
||
let arr_val = self.regs[b];
|
||
if arr_val.is_object() {
|
||
let arr_obj = unsafe { &*arr_val.as_js_object_ptr() };
|
||
if arr_obj.is_array() {
|
||
for i in 0..arr_obj.array_prop_count {
|
||
let v = arr_obj.get_prop_at(i);
|
||
let si = self.property_key_si(v)?;
|
||
excluded.insert(si);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
let src_obj = unsafe { &*src.as_js_object_ptr() };
|
||
|
||
// 收集-提交:先取齐 (键, 值),取值阶段触发 getter(可能抛异常),再统一写
|
||
// 目标对象,避免提交写与取值互相交错。只复制可枚举自有属性(CopyDataProperties)。
|
||
// 字符串包装对象的索引字符是构造期物化的可枚举自有属性,走同一 walk 路径。
|
||
let mut assignments: Vec<(u32, JsValue)> = Vec::new();
|
||
|
||
// 自身属性:walk_own_keys 已合并数组元素区(hole 跳过)并返回绝对存储索引,
|
||
// 仅可枚举,跳过 pattern 已绑定的键。
|
||
let keys = oxide_builtins::object::walk_own_keys(self, src_obj);
|
||
for (si, pos) in keys {
|
||
let enumerable = src_obj
|
||
.prop_meta_at(pos)
|
||
.map(|m| m.attributes.enumerable())
|
||
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
|
||
if !enumerable {
|
||
continue;
|
||
}
|
||
if excluded.contains(&si) {
|
||
continue;
|
||
}
|
||
let val = match self.ordinary_get(src_obj, si, src) {
|
||
Ok(v) => v,
|
||
Err(e) => return self.raise_call_error(&e).map(|_| ()),
|
||
};
|
||
assignments.push((si, val));
|
||
}
|
||
|
||
// 提交到 rest 对象。
|
||
let proto_ptr = self.realm.session.borrow().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||
let rest_ptr = self.alloc_object(JsObject::new_empty(
|
||
oxide_kernel::shape_forge::EMPTY_SHAPE_ID,
|
||
JsValue::from_js_object(proto_ptr),
|
||
));
|
||
let rest = unsafe { &mut *rest_ptr };
|
||
for (si, val) in assignments {
|
||
self.set_or_create_prop_value(rest, si, val);
|
||
}
|
||
self.regs[rd] = JsValue::from_js_object(rest_ptr);
|
||
Ok(())
|
||
}
|
||
|
||
/// SPREAD_OBJECT:对象字面量 `...` 展开,把源的可枚举自有属性写入目标对象(原地改)。
|
||
///
|
||
/// 语义 = CopyDataProperties 的"非 null/undefined 源"分支:`{...null}` / `{...undefined}`
|
||
/// 合法(产出空展开),与 REST_OBJECT 对 null/undefined 抛 TypeError 不同。
|
||
///
|
||
/// # 步骤
|
||
/// 1. null/undefined 源直接返回(空展开)
|
||
/// 2. 字符串源按 UTF-16 code unit 下标复制为可枚举索引属性
|
||
/// 3. 对象源先收集可枚举自有属性(数组元素区 + shape 链),取值经 ordinary_get
|
||
/// 触发访问器 getter,再统一写入目标
|
||
///
|
||
/// # 边界与前提
|
||
/// - 其它原始值(number/boolean/symbol)无自有可枚举属性 → 空展开
|
||
/// - 目标已有同名属性被覆盖(后定义/后展开者胜)
|
||
///
|
||
/// # 副作用
|
||
/// - 原地修改 rd 指向的对象;取值可能触发 getter 并抛异常(异常传播)
|
||
pub(crate) fn dispatch_spread_object(&mut self, rd: usize, a: usize) -> Result<(), String> {
|
||
vm_trace!("SPREAD_OBJECT rd={}", rd);
|
||
let target_val = self.regs[rd];
|
||
let src = self.regs[a];
|
||
|
||
// null/undefined 源合法:展开为空,直接返回。
|
||
if src.is_null() || src.is_undefined() {
|
||
return Ok(());
|
||
}
|
||
|
||
// 字符串源:按单元下标复制(可枚举索引属性,lone surrogate 保真)。
|
||
if src.is_string() {
|
||
let target = unsafe { &mut *target_val.as_js_object_ptr() };
|
||
// SAFETY: src 是字符串值。
|
||
let code_units: Vec<u16> = unsafe { (*src.as_string_ptr()).units().into_owned() };
|
||
for (i, unit) in code_units.iter().enumerate() {
|
||
let si = make_int_key(i as u32);
|
||
let ch_val = self.unit_char_value(*unit);
|
||
self.set_or_create_prop_value(target, si, ch_val);
|
||
}
|
||
return Ok(());
|
||
}
|
||
|
||
// 其它原始值无自有可枚举属性 → 空展开。
|
||
if !src.is_object() {
|
||
return Ok(());
|
||
}
|
||
|
||
let src_obj = unsafe { &*src.as_js_object_ptr() };
|
||
|
||
// 收集-提交:先取齐 (键, 值),取值阶段触发 getter(可能抛异常),
|
||
// 再统一写目标,避免提交写与取值互相交错。
|
||
let mut assignments: Vec<(u32, JsValue)> = Vec::new();
|
||
|
||
// 自身属性:walk_own_keys 已合并数组元素区(hole 跳过)并返回绝对存储索引,
|
||
// 仅可枚举;取值经 ordinary_get 触发访问器 getter。
|
||
let keys = oxide_builtins::object::walk_own_keys(self, src_obj);
|
||
for (si, pos) in keys {
|
||
let enumerable = src_obj
|
||
.prop_meta_at(pos)
|
||
.map(|m| m.attributes.enumerable())
|
||
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
|
||
if !enumerable {
|
||
continue;
|
||
}
|
||
let val = self.ordinary_get(src_obj, si, src)?;
|
||
assignments.push((si, val));
|
||
}
|
||
|
||
// 提交:覆盖目标已有同名属性(从左到右求值,后展开者胜)。
|
||
let target = unsafe { &mut *target_val.as_js_object_ptr() };
|
||
for (si, val) in assignments {
|
||
self.set_or_create_prop_value(target, si, val);
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// GET_TEMPLATE_OBJECT:GetTemplateObject 语义——按 (模块 flat_id, site 序号)
|
||
/// 查缓存,未命中则构建模板对象(cooked/raw 数组 + raw 属性)并冻结。
|
||
///
|
||
/// # ext 布局(紧跟指令后的扩展字)
|
||
/// - `n`:quasis 段数;
|
||
/// - 每段两个字:cooked 字(高位 `0x8000_0000` 标记非法转义 → undefined,
|
||
/// 低 31 位为常量池下标)、raw 字(常量池下标);
|
||
/// - 末尾:site 序号。
|
||
///
|
||
/// # 副作用
|
||
/// - 首次构建时分配 cooked/raw 数组并写入 `template_objects` 缓存(GC 根);
|
||
/// 缓存命中时零分配。
|
||
pub(crate) fn dispatch_get_template_object(&mut self, rd: usize) -> Result<(), String> {
|
||
vm_trace!("GET_TEMPLATE_OBJECT rd={}", rd);
|
||
let n = self.bytecode[self.pc] as usize;
|
||
self.pc += 1;
|
||
let mut cooked_words = Vec::with_capacity(n);
|
||
let mut raw_idxs = Vec::with_capacity(n);
|
||
for _ in 0..n {
|
||
cooked_words.push(self.bytecode[self.pc]);
|
||
self.pc += 1;
|
||
raw_idxs.push(self.bytecode[self.pc]);
|
||
self.pc += 1;
|
||
}
|
||
let site_no = self.bytecode[self.pc];
|
||
self.pc += 1;
|
||
|
||
// 键含表代际:跨 run 调用的旧代模块与当前 run 模块 flat_id 重编号,
|
||
// 无代际维度会误命中他代同 (flat_id, site) 的模板对象。
|
||
let key = (self.active_table_gen, self.active_flat_id, site_no);
|
||
if let Some(&cached) = self.template_objects.get(&key) {
|
||
self.regs[rd] = cached;
|
||
return Ok(());
|
||
}
|
||
|
||
let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||
let proto_val = JsValue::from_js_object(proto_ptr);
|
||
// 模板对象直接分配为 session 对象:写入 session 根(存进 global/数组)
|
||
// 时对象地址稳定,缓存指针不失效——分配档不同则首次写入触发搬移,
|
||
// 缓存旧指针与新实例分叉,同一 site 两次取值将返回不同对象。
|
||
let mut alloc_session_array = |n: usize| {
|
||
let clone = JsObject::new_array(oxide_kernel::shape_forge::EMPTY_SHAPE_ID, proto_val, n);
|
||
self.alloc_session_object(clone)
|
||
};
|
||
let cooked = alloc_session_array(n);
|
||
let raw = alloc_session_array(n);
|
||
let cooked_val = JsValue::from_js_object(cooked);
|
||
let raw_val = JsValue::from_js_object(raw);
|
||
|
||
// 元素:可枚举、不可写、不可配置(模板对象属性恒只读,禁止改写元素)。
|
||
let elem_attrs = PropAttributes::new(false, true, false);
|
||
for i in 0..n {
|
||
let (c_val, r_val) = {
|
||
// immutables 借用限于本块:先取出本段两值,再放行借用。
|
||
let imm = self.immutables();
|
||
let c_val = if cooked_words[i] & 0x8000_0000 != 0 {
|
||
JsValue::undefined()
|
||
} else {
|
||
imm.get(cooked_words[i] as usize).copied().unwrap_or(JsValue::undefined())
|
||
};
|
||
let r_val = imm.get(raw_idxs[i] as usize).copied().unwrap_or(JsValue::undefined());
|
||
(c_val, r_val)
|
||
};
|
||
// SAFETY: cooked/raw 为本函数刚分配的 epoch 对象,借用仅在本循环内消费。
|
||
unsafe {
|
||
(*cooked).set_prop_at(i, c_val);
|
||
(*cooked).set_data_meta(i, elem_attrs);
|
||
(*raw).set_prop_at(i, r_val);
|
||
(*raw).set_data_meta(i, elem_attrs);
|
||
}
|
||
}
|
||
|
||
// raw 属性:不可枚举、不可写、不可配置(规范 desc 无 writable/enumerable
|
||
// 键,DefinePropertyOrThrow 默认 false)。先于冻结定义(冻结后不可扩展,
|
||
// 新增属性会被拒)。
|
||
let raw_si = self.kernel_core.perm_interner().intern("raw").0;
|
||
// SAFETY: cooked 为本函数分配的 epoch 对象,借用仅在本次 define 内消费。
|
||
self.define_data_property(unsafe { &mut *cooked }, raw_si, raw_val, PropAttributes::new(false, false, false))?;
|
||
|
||
// 冻结两个数组:不可扩展 + length writable=false(is_frozen 使 length
|
||
// 赋值与元素写入经 ordinary_set 拒绝);length {e:false,w:false,c:false}
|
||
// 由 getOwnPropertyDescriptor 的数组 length 特判表达。
|
||
// SAFETY: cooked/raw 为本函数分配的 session 对象。
|
||
unsafe {
|
||
(*cooked).set_frozen(true);
|
||
(*cooked).set_extensible(false);
|
||
(*raw).set_frozen(true);
|
||
(*raw).set_extensible(false);
|
||
}
|
||
self.template_objects.insert(key, cooked_val);
|
||
self.regs[rd] = cooked_val;
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
use std::sync::Arc;
|
||
|
||
use oxide_bytecode::module::CompiledModule;
|
||
use oxide_bytecode::opcode;
|
||
|
||
use crate::vm_state::ForOfEntry;
|
||
use crate::vm_support::FunctionFlags;
|
||
|
||
#[test]
|
||
fn for_of_close_pops_iterator_stack() {
|
||
let module = CompiledModule {
|
||
bytecode: Arc::from(vec![
|
||
opcode::encode(opcode::OpCode::FOR_OF_CLOSE, 0, 0, 0),
|
||
opcode::encode(opcode::OpCode::HALT, 0, 0, 0),
|
||
]),
|
||
n_registers: 1,
|
||
..CompiledModule::new()
|
||
};
|
||
let mut vm = Vm::new();
|
||
vm.iters.for_of_iters.push(ForOfEntry {
|
||
iterator: JsValue::undefined(),
|
||
last_result: JsValue::undefined(),
|
||
is_async: false,
|
||
fast: None,
|
||
fast_value: JsValue::undefined(),
|
||
fast_inner: JsValue::undefined(),
|
||
fast_cursor: 0,
|
||
});
|
||
|
||
vm.run(&Arc::new(module))
|
||
.expect("FOR_OF_CLOSE should tolerate non-object sentinel");
|
||
|
||
assert!(vm.iters.for_of_iters.is_empty());
|
||
}
|
||
|
||
#[test]
|
||
fn dispatch_new_expression_param_overlap_reads_spill_first() {
|
||
// NEW 收敛路径同款重叠几何(实参源 regs[1..3) 与 callee 形参写入区 regs[2..4)
|
||
// 重叠,first < param_base):经 NEW_EXPRESSION 入口压帧,形参与 spill 实参区
|
||
// (arguments 对象源)必须取原始实参值,不得先写后读串值。
|
||
let mut vm = Vm::new();
|
||
// sub_modules[1] = 构造器:2 个形参,param_base=2(与调用方实参槽 2 重叠)
|
||
let mut ctor_mod = CompiledModule::new();
|
||
ctor_mod.n_args = 2;
|
||
ctor_mod.param_base = 2;
|
||
ctor_mod.n_registers = 5;
|
||
ctor_mod.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::RETURN, 0, 0, 0)]);
|
||
vm.install_module_table_for_test(Arc::new(vec![Arc::new(CompiledModule::new()), Arc::new(ctor_mod)]));
|
||
vm.active_reg_limit = 8;
|
||
// NEW_EXPRESSION 指令:ext 低 8 位 = 实参个数 2,高 8 位 = 窗口 0(全量)
|
||
vm.bytecode = Arc::from(vec![opcode::encode(opcode::OpCode::NEW_EXPRESSION, 0, 0, 0), 2]);
|
||
vm.pc = 0;
|
||
// 调用方实参区 regs[1..3):arg0=10, arg1=20;regs[2] 同时是 callee 形参槽(param_base=2)
|
||
vm.regs[1] = JsValue::int(10);
|
||
vm.regs[2] = JsValue::int(20);
|
||
vm.regs[5] = vm.create_function_object(1, vm.current_gen, FunctionFlags::none());
|
||
|
||
vm.dispatch_new_expression(0, 5, 1).expect("NEW 压帧成功");
|
||
|
||
let frame = vm.frames.last().expect("压帧后应有帧");
|
||
// 形参从 spill 实参区取源:regs[2]=arg0=10,regs[3]=arg1=20(不得被先写覆盖)
|
||
assert_eq!(vm.regs[2], JsValue::int(10), "形参 a 应为实参 arg0");
|
||
assert_eq!(vm.regs[3], JsValue::int(20), "形参 b 应为实参 arg1(不受先写覆盖)");
|
||
// spill 实参区(arguments 对象源)保持原实参值
|
||
let base = frame.arguments_base as usize;
|
||
assert_eq!(vm.spill_stack[base], JsValue::int(10), "spill 实参区 arg0");
|
||
assert_eq!(vm.spill_stack[base + 1], JsValue::int(20), "spill 实参区 arg1");
|
||
// NEW 帧契约:构造结果寄存器与 constructed_this 随帧携带
|
||
assert_eq!(frame.construct_result_reg, Some(0), "构造结果写回 regs[0]");
|
||
assert!(
|
||
frame.constructed_this.is_some_and(|v| v.is_object()),
|
||
"基类构造路径 constructed_this 应为新对象"
|
||
);
|
||
assert!(!frame.is_derived_constructor, "普通函数非 derived 构造器");
|
||
}
|
||
}
|