use crate::vm::Vm; use crate::{vm_error, vm_trace}; use oxide_bytecode::opcode; use oxide_runtime_api as coercion; use oxide_kernel::shape_forge::EMPTY_SHAPE_ID; use oxide_types::arguments_map::ArgumentsMapState; use oxide_types::mem::P; use oxide_types::object::{JsObject, PropAttributes}; use oxide_types::private_key::{encode_symbol_key, make_int_key}; use oxide_types::value::{JsType, JsValue}; impl Vm { #[inline(always)] pub(crate) fn dispatch_load_const(&mut self, rd: usize, instr: u32) -> Result<(), String> { let idx = (instr >> 16) as usize; vm_trace!("LOAD_CONST rd={} idx={}", rd, idx); let imm = self.immutables(); if idx < imm.len() { self.regs[rd] = imm[idx]; Ok(()) } else { vm_error!("LOAD_CONST index {} out of bounds (len={})", idx, imm.len()); Err(format!("constant index {idx} out of bounds")) } } /// 全局对象属性读(含访问器 getter 调用):命中属性时若为访问器则以 /// 全局对象为 receiver 调用 getter(getter 副作用先于后续写生效),返回 /// getter 结果;命中数据属性返回槽值;未命中返回 None。getter 抛错经 /// `raise_call_error` 就地展开(pc 已改写),调用方须按 pc 变化停行。 fn global_prop_read_with_getter(&mut self, global: &P, si: u32) -> Result, String> { let Some(val) = self.resolve_property(global, si) else { return Ok(None); }; let Some(getter) = self.resolve_property_getter(global, si) else { return Ok(Some(val)); }; // SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。 let global_val = JsValue::from_js_object(global.as_ptr() as *mut JsObject); match self.call_function_sync(getter, global_val, &[]) { Ok(v) => Ok(Some(v)), Err(err) => { self.raise_call_error(&err)?; Ok(None) } } } /// 未声明标识符读:查 global object 属性存在性,命中取属性值,未命中抛 ReferenceError。 /// /// # 步骤 /// 1. imm16 取常量池 key(标识符名字符串),解析为属性键 si。 /// 2. `resolve_property` 沿 shape 链 + 原型链解析 global 属性(覆盖全部属性存储)。 /// 3. 命中写 rd;未命中 raise ReferenceError(`{name} is not defined`)。 /// /// # 边界与前提 /// - key 常量必须是字符串(emit 侧保证);非字符串按错误返回。 /// - 属性存在但值为 undefined(显式赋 undefined)时返回 undefined,不抛。 pub(crate) fn dispatch_load_global(&mut self, rd: usize, instr: u32) -> Result { let idx = (instr >> 16) as usize; vm_trace!("LOAD_GLOBAL rd={} idx={}", rd, idx); let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined()); if !key_val.is_string() { return Err(format!("LOAD_GLOBAL constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; let global = { let session = self.realm.session.borrow(); session.global_object().clone() }; let pc_before = self.pc; let hit = self.global_prop_read_with_getter(&global, si)?; if self.pc != pc_before { // getter 在途抛错已展开到 catch/finally 入口:pc 由处理器接管, // 不得续行(结果寄存器不得再写、不得抛未解析引用)。 return Ok(true); } if let Some(val) = hit { self.regs[rd] = val; Ok(false) } else { // SAFETY: key_val 已校验为字符串值;错误消息走 lossy 文本(不可解析名 // 含孤立 surrogate 时映射为 FFFD,仅为展示用途)。 let name = unsafe { (*key_val.as_string_ptr()).as_lossy_str() }; self.raise_error_kind("ReferenceError", &format!("{name} is not defined"))?; Ok(true) } } /// typeof 未声明标识符读:同 [`dispatch_load_global`] 查 global object 属性, /// 但未命中求值为 undefined(IsUnresolvableReference)而非抛 ReferenceError。 /// /// # 步骤 /// 1. imm16 取常量池 key(标识符名字符串),解析为属性键 si。 /// 2. `resolve_property` 沿 shape 链 + 原型链解析 global 属性。 /// 3. 命中写 rd;未命中写 undefined。 pub(crate) fn dispatch_load_global_typeof(&mut self, rd: usize, instr: u32) -> Result<(), String> { let idx = (instr >> 16) as usize; vm_trace!("LOAD_GLOBAL_TYPEOF rd={} idx={}", rd, idx); let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined()); if !key_val.is_string() { return Err(format!("LOAD_GLOBAL_TYPEOF constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; let global = { let session = self.realm.session.borrow(); session.global_object().clone() }; // typeof 访问器属性同样触发 getter(getter 抛错时 typeof 不豁免,异常照抛)。 let pc_before = self.pc; let hit = self.global_prop_read_with_getter(&global, si)?; if self.pc != pc_before { // getter 在途抛错已展开:pc 由处理器接管,不得续行。 return Ok(()); } self.regs[rd] = hit.unwrap_or(JsValue::undefined()); Ok(()) } /// 全局词法环境读:先查 session 全局词法环境镜像(顶层 let/const 真源), /// 命中取值;未命中回退全局对象属性读,缺失抛 ReferenceError。 /// /// # 步骤 /// 1. imm16 取常量池 key(标识符名字符串),解析为属性键 si。 /// 2. 查镜像对象属性,命中写 rd 返回。 /// 3. 未命中回退全局对象属性读,命中写 rd 返回;缺失 raise ReferenceError。 /// /// # 边界与前提 /// - key 常量必须是字符串(emit 侧保证);非字符串按错误返回。 /// - 镜像命中即返回(顶层词法绑定优先于全局对象属性,规范全局环境词法面)。 pub(crate) fn dispatch_load_global_lex(&mut self, rd: usize, instr: u32) -> Result { let idx = (instr >> 16) as usize; vm_trace!("LOAD_GLOBAL_LEX rd={} idx={}", rd, idx); let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined()); if !key_val.is_string() { return Err(format!("LOAD_GLOBAL_LEX constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; // 镜像优先:顶层 let/const 真源,命中即返回。 let lexical_hit = { let session = self.realm.session.borrow(); self.resolve_property(session.global_lexical(), si) }; if let Some(val) = lexical_hit { self.regs[rd] = val; return Ok(false); } // 回退全局对象属性读(未声明名与 known builtin 名同形 LOAD_GLOBAL); // 访问器属性调用 getter(receiver = 全局对象),裸读必须触发 getter。 let global = { let session = self.realm.session.borrow(); session.global_object().clone() }; let pc_before = self.pc; let global_hit = self.global_prop_read_with_getter(&global, si)?; if self.pc != pc_before { // getter 在途抛错已展开到 catch/finally 入口:pc 由处理器接管, // 不得续行(结果寄存器不得再写、不得抛未解析引用)。 return Ok(true); } if let Some(val) = global_hit { self.regs[rd] = val; Ok(false) } else { // SAFETY: key_val 已校验为字符串值;错误消息走 lossy 文本。 let name = unsafe { (*key_val.as_string_ptr()).as_lossy_str() }; self.raise_error_kind("ReferenceError", &format!("{name} is not defined"))?; Ok(true) } } /// 全局词法环境写:把 a 槽值写入镜像对象(顶层 let/const 声明与赋值双写)。 /// /// # 步骤 /// 1. ext[0] 取键常量池下标,解析为属性键 si。 /// 2. 镜像对象由 session 解析,`set_or_create_prop_value` 写值(缺失建属性)。 /// /// # 副作用 /// - 修改镜像对象 shape/属性表/generation。 pub(crate) fn dispatch_store_global_lex(&mut self, a: usize, key_idx: u16) -> Result<(), String> { let idx = key_idx as usize; vm_trace!("STORE_GLOBAL_LEX value={} idx={}", a, idx); let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined()); if !key_val.is_string() { return Err(format!("STORE_GLOBAL_LEX constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; let value = self.regs[a]; let lexical_ptr = self.realm.session.borrow().global_lexical().as_ptr() as *mut JsObject; // SAFETY: 镜像对象钉在 session 永久区,指针在 VM 生命周期内有效。 let obj = unsafe { &mut *lexical_ptr }; self.set_or_create_prop_value(obj, si, value); Ok(()) } #[inline(always)] pub(crate) fn dispatch_typeof(&mut self, rd: usize, a: usize) { vm_trace!("TYPEOF rd={} r{}={:?}", rd, a, self.regs[a]); let val = self.regs[a]; let kind = match val.js_type() { JsType::Undefined => 0, JsType::Null => 1, JsType::Bool => 2, JsType::Int | JsType::Double => 3, JsType::String => 4, JsType::Symbol => 5, JsType::BigInt => 6, JsType::Object => { // [[IsHTMLDDA]] 宿主对象按 "undefined" 报告(B.3.4,Type 仍是 object); // 宿主对象可调用,判定须先于函数分支,typeof 仍报 "undefined"。 // 其余函数对象归为 "function"(语言类型是 object,typeof 有专属分支)。 let obj = unsafe { &*val.as_js_object_ptr() }; if obj.is_html_dda_obj() { 0 } else if obj.is_function() { 7 } else { 1 } } }; // 结果字符串复用进程级静态表(零分配、无锁、跨 VM 共享), // 避免每次 typeof 新建 session 字符串。 self.regs[rd] = JsValue::string(oxide_kernel::string_forge::typeof_string_ptr(kind)); } /// ToObject:rd 原地转换。对象直接返回;null/undefined 抛 TypeError; /// 其余原始值包装为对应包装对象(with 对象环境需要)。 #[inline(always)] pub(crate) fn dispatch_to_object(&mut self, rd: usize) -> Result<(), String> { vm_trace!("TO_OBJECT rd={} r{}={:?}", rd, rd, self.regs[rd]); let val = self.regs[rd]; if val.is_object() { return Ok(()); } if val.is_null() || val.is_undefined() { // 抛 JS 异常而非返回 Err:外围 try/catch 须能捕获(对象解构空 pattern)。 self.raise_error_kind("TypeError", "Cannot convert null or undefined to object")?; return Ok(()); } let obj_val = oxide_runtime_api::to_object(val, self)?; self.regs[rd] = obj_val; Ok(()) } /// ToPropertyKey:推导属性键值。Symbol 原样返回;对象经 ToPrimitive(string /// hint) 后 ToString;其余原始值直接 ToString。 /// /// # 边界与前提 /// - 对象键的 toString/valueOf 调用有用户副作用,可能抛异常;抛错经 /// `raise_call_error` 就地展开(pc 已改写),调用方须按 pc 变化停行。 /// /// # 副作用 /// - 成功路径返回推导后的字符串/Symbol 值;抛错路径经 `unwind` 改写 /// pc / 异常通道并返回 undefined(调用方不得再使用)。 pub(crate) fn derive_property_key_value(&mut self, val: JsValue) -> Result { vm_trace!("derive_property_key_value: {:?}", val); if val.is_symbol() { return Ok(val); } let pc_before = self.pc; let prim = if val.is_object() { coercion::to_primitive(val, coercion::ToPrimitiveHint::String, self)? } else { val }; if self.pc != pc_before { // ToPrimitive 在途抛错已展开:pc 由处理器接管,结果值不得使用。 return Ok(JsValue::undefined()); } if prim.is_symbol() { return Ok(prim); } let units = oxide_runtime_api::to_units_full(prim, self)?; Ok(self.new_string_units_owned(units)) } pub(crate) fn dispatch_load_var(&mut self, rd: usize, a: usize) -> Result { vm_trace!("LOAD_VAR rd={} r{}={:?}", rd, a, self.regs[a]); // a==254 即读 this:derived 构造帧在 super() 成功前读 this 抛 ReferenceError。 if a == 254 && self .frames .last() .map(|frame| frame.is_derived_constructor && !frame.super_called) .unwrap_or(false) { self.raise_error_kind("ReferenceError", "must call super constructor before using 'this'")?; return Ok(true); } self.regs[rd] = self.regs[a]; Ok(false) } pub(crate) fn dispatch_store_var(&mut self, rd: usize, a: usize, b: usize) -> Result { vm_trace!("STORE_VAR r{}={:?} const={}", rd, self.regs[a], b); if b != 0 { // const guard:检查目标槽是否已初始化。 if !self.regs[rd].is_undefined() { self.raise_error_kind("TypeError", "Assignment to constant variable")?; return Ok(true); } } self.regs[rd] = self.regs[a]; // mapped arguments 参数直写同步:目标寄存器是创建帧的存活映射参数时, // 把新值回写 arguments 存储值(读路径权威源),嵌套调用覆盖寄存器后 // 存储值仍正确。 self.sync_mapped_arguments_param(rd); Ok(false) } /// mapped arguments 参数寄存器直写同步:目标寄存器是顶帧 mapped arguments 的 /// 存活映射参数时,把新值回写 arguments 对象存储值(读路径权威源)。 /// /// # 边界与前提 /// - 仅顶帧带 mapped arguments 对象(`frame.arguments_obj` 非 undefined)时 /// 生效;其余帧首检即返回(热路径零开销)。 /// - 映射失效(创建帧已弹出)或索引已 unmapped(收窄不可写 / 删除)时不同步。 /// /// # 副作用 /// - 写 arguments 对象存储值槽(shape 槽已存在,无 shape 变更)。 pub(crate) fn sync_mapped_arguments_param(&mut self, rd: usize) { let Some(frame) = self.frames.last() else { return; }; if frame.arguments_obj.is_undefined() { return; } let obj_ptr = frame.arguments_obj.as_js_object_ptr(); if obj_ptr.is_null() { return; } // SAFETY: arguments_obj 是 GC 根(帧遍历期标记),session 内指针有效。 if !unsafe { (*obj_ptr).is_arguments_obj() } { return; } let state_ptr = unsafe { (*obj_ptr).native_data() } as *const ArgumentsMapState; if state_ptr.is_null() { return; } // SAFETY: native_data 由创建期 Box::into_raw 分配,生命周期与对象一致。 let state = unsafe { &*state_ptr }; if !self.arguments_mapping_alive(state) { return; } let param_base = state.param_base as usize; let param_count = state.param_count as usize; if rd < param_base || rd >= param_base + param_count { return; } let index = (rd - param_base) as u16; if !state.is_mapped(index) { return; } let si = make_int_key(index as u32); let val = self.regs[rd]; // SAFETY: obj_ptr 是有效对象指针(GC 根)。 let obj_mut = unsafe { &mut *obj_ptr }; self.set_or_create_prop_value(obj_mut, si, val); } /// SPILL:`regs[rd]` → `spill_stack[帧基址 + slot]`。 /// /// ext 字为 slot(u16);帧基址 = 当前 CallFrame.spill_offset(顶层模块无帧为 0)。 /// # 边界与前提 /// - slot 超 u16 范围返回错误(RegAlloc 保证发射在界内)。 /// # 副作用 /// - 写 spill 栈,可能触发 resize 扩容。 #[inline(always)] pub(crate) fn dispatch_spill(&mut self, rd: usize) -> Result<(), String> { let slot = self.bytecode[self.pc] as usize; self.pc += 1; if slot > u16::MAX as usize { return Err(format!("SPILL slot {slot} out of range (max 65535)")); } let base = self.frames.last().map(|f| f.spill_offset as usize).unwrap_or(0); let idx = base + slot; if idx >= self.spill_stack.len() { self.spill_stack.resize(idx + 1, JsValue::undefined()); } vm_trace!("SPILL r{} -> slot[{}] base={}", rd, slot, base); self.spill_stack[idx] = self.regs[rd]; Ok(()) } /// UNSPILL:`spill_stack[帧基址 + slot]` → `regs[rd]`。 /// /// # 边界与前提 /// - slot 超 u16 范围返回错误。 /// - 越界读返回 undefined(防御:slot 由 RegAlloc 保证在界内)。 #[inline(always)] pub(crate) fn dispatch_unspill(&mut self, rd: usize) -> Result<(), String> { let slot = self.bytecode[self.pc] as usize; self.pc += 1; if slot > u16::MAX as usize { return Err(format!("UNSPILL slot {slot} out of range (max 65535)")); } let base = self.frames.last().map(|f| f.spill_offset as usize).unwrap_or(0); let idx = base + slot; vm_trace!("UNSPILL r{} <- slot[{}] base={}", rd, slot, base); self.regs[rd] = if idx < self.spill_stack.len() { self.spill_stack[idx] } else { JsValue::undefined() }; Ok(()) } /// 创建空对象;带键常量表时(a 槽 = 属性数 ≤255,ext = 每键常量池下标)按纯静态 /// 数据键前缀批量构造:单次链式 `make_shape` 预建整条 shape、预分配全部数据槽, /// 后续属性值经 SET_PROP_BATCH 纯槽写。 /// /// # 步骤 /// 1. 逐键直读模块 si 侧表(模块装载期 `activate_immutables` 已预 intern, /// 与逐属性 SET_PROP 路径逐字节同键),链式 `make_shape` 累加 shape。 /// 2. 分配对象、预填充 `nprops` 个数据槽、`generation += nprops`(与逐属性路径终值一致)。 /// /// # 边界与前提 /// - `nprops = 0` 时退化为普通空对象(无 ext 键表)。 /// - 侧表条目缺失(防御面,正常路径不出现)回退旧路径按键值重推 si。 #[inline(always)] pub(crate) fn dispatch_new_object(&mut self, rd: usize, instr: u32) -> Result<(), String> { let nprops = opcode::a(instr) as usize; vm_trace!("NEW_OBJECT rd={} nprops={}", rd, nprops); let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let mut shape_id = EMPTY_SHAPE_ID; if nprops > 0 { let key_idxs: Vec = self.bytecode[self.pc..self.pc + nprops].to_vec(); self.pc += nprops; // si 侧表与 immutables 同点激活:活动模块的侧表此刻必已填充。 let si_table = self.active_table().si_tables[self.active_flat_id as usize] .get() .expect("活动模块的 si 侧表已随不可变常量激活同步填充"); let key_si: Vec> = key_idxs.iter().map(|k| si_table.get(*k as usize).copied().flatten()).collect(); for (key_idx, si_opt) in key_idxs.iter().zip(key_si.iter()) { let si = match si_opt { Some(si) => *si, None => { let imm = self.immutables(); let key_val = imm.get(*key_idx as usize).copied().unwrap_or(JsValue::undefined()); self.property_key_si(key_val)? } }; shape_id = self.kernel_core.shape_forge().make_shape(shape_id, si); } } let obj = self.alloc_object(JsObject::new_empty(shape_id, JsValue::from_js_object(proto_ptr))); // 预分配数据槽并同步 generation:批量一次性 +nprops,与逐属性路径的终值一致。 let obj_ref = unsafe { &mut *obj }; for _ in 0..nprops { obj_ref.push_prop(JsValue::undefined()); obj_ref.bump_generation(); } self.regs[rd] = JsValue::object(obj as *mut u8); Ok(()) } /// 创建 session 直分的空普通对象([[Prototype]] = Object.prototype):与 /// `create_function_object` 的 prototype 子对象同策略——若按 epoch 分配,写入 /// 全局等逃逸根时晋升屏障会深克隆进 session,持有者(构造器 prototype 属性) /// 与克隆体指针分裂、严格相等恒 false。 /// /// # 副作用 /// - 登记 session 对象表并计入堆账目;回收由 session GC mark/sweep 承担。 pub(crate) fn dispatch_new_session_object(&mut self, rd: usize) -> Result<(), String> { vm_trace!("NEW_SESSION_OBJECT rd={}", rd); let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)); let obj_ptr = self.alloc_session_object(obj); self.regs[rd] = JsValue::object(obj_ptr as *mut u8); Ok(()) } /// 创建 arguments 对象:索引属性取当前帧(或 inline 同步调用)的完整实参, /// 附 length / callee / @@iterator 属性。索引与形参不同步(mapped 同步语义 /// 未实现);callee 按 strict ‖ !simple 分流为受限访问器或数据属性。 /// /// # 边界与前提 /// - 实参区由统一压帧入口 `push_bytecode_frame` 在推帧时写入 spill 栈; /// frames 为空(inline 同步调用)时读 `inline_args_*`。 /// - 索引属性用 shape 槽存储(普通对象),length 为不可枚举数据属性。 pub(crate) fn dispatch_create_arguments(&mut self, rd: usize) -> Result<(), String> { let (base, count) = match self.frames.last() { Some(frame) => (frame.arguments_base, frame.arguments_count), None => (self.inline_args_base, self.inline_args_count), }; let proto_ptr = self.realm.object_prototype.borrow().as_ptr() as *mut JsObject; let obj_ptr = self.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr))); let obj = unsafe { &mut *obj_ptr }; obj.type_tag = JsObject::OBJ_TYPE_ARGUMENTS; // mapped 判定:sloppy 且 simple(strict ‖ !simple → unmapped)。mapped 时 // 建同步状态盒(存 native_data):参数寄存器与 arguments 存储值双向同步, // 帧身份经 (帧下标, 帧身份) 判定创建帧是否仍在栈上。unmapped 时 native_data // 保持 null,exotic 四臂短路,行为与现树一致。 let strict = self.current_strict(); let simple = self.active_module().is_some_and(|m| m.has_simple_params); if !strict && simple { if let (Some(frame_id), Some(module)) = (self.frames.last().map(|f| f.frame_id), self.active_module()) { let param_base = module.param_base; let param_count = module.n_args as u16; let words = (param_count as usize).div_ceil(64).max(1); let mut mapped_mask = vec![u64::MAX; words]; let valid_bits = param_count as usize % 64; if valid_bits != 0 { mapped_mask[words - 1] &= (1u64 << valid_bits) - 1; } else if param_count == 0 { mapped_mask[0] = 0; } let state = ArgumentsMapState { param_base, param_count, mapped_mask, frame_depth: (self.frames.len() - 1) as u32, frame_id, }; obj.set_native_data(Box::into_raw(Box::new(state)) as *mut u8); // 帧锚点:参数寄存器直写(STORE_VAR)据此同步 arguments 存储值, // 使嵌套调用期参数寄存器被覆盖时存储值仍为权威读源。 if let Some(frame) = self.frames.last_mut() { frame.arguments_obj = JsValue::from_js_object(obj_ptr); } } } // 索引属性:按实参下标写入 shape 槽,属性描述符为默认(可写/可枚举/可配置)。 // 下标走整数键,保证 `arguments[0]`(property_key_si(int 0))键等价命中。 for i in 0..count as usize { let si = make_int_key(i as u32); let val = self.spill_stack.get(base as usize + i).copied().unwrap_or(JsValue::undefined()); self.set_or_create_prop_value(obj, si, val); } // length:实参个数,可写、不可枚举、可配置。 let length_si = self.length_si; if let Err(msg) = self.define_data_property( obj, length_si, JsValue::int(count as i32), PropAttributes::new(true, false, true), ) { return self.raise_error_kind("TypeError", &msg); } // callee:按 strict 与 simple 参数列表四分流(规范 CreateMappedArgumentsObject / // CreateUnmappedArgumentsObject):strict ‖ !simple → 受限访问器(get/set // 共享 %ThrowTypeError%,访问即抛);sloppy simple → 数据属性(值 = 当前 // 函数本身,可写、不可枚举、可配置)。 let callee_si = self.kernel_core.perm_interner().intern("callee").0; let callee = self.current_callee().unwrap_or(JsValue::undefined()); if strict || !simple { // SAFETY: 指针由绑定层在 session 构造期写入,session 存活期内有效。 let thrower_ptr = self.realm.session.borrow().builtin_world().throw_type_error.get(); if thrower_ptr.is_null() { // 绑定层尚未写入共享对象(正常路径不出现):退化为数据属性保调用可用。 if let Err(msg) = self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true)) { return self.raise_error_kind("TypeError", &msg); } } else { // SAFETY: thrower_ptr 由绑定层写入的存活对象,转 *mut 供 from_js_object 消费。 let thrower_val = JsValue::from_js_object(unsafe { &*thrower_ptr } as *const JsObject as *mut JsObject); if let Err(msg) = self.define_accessor_property( obj, callee_si, thrower_val, thrower_val, PropAttributes::new(false, false, false), ) { return self.raise_error_kind("TypeError", &msg); } } } else if let Err(msg) = self.define_data_property(obj, callee_si, callee, PropAttributes::new(true, false, true)) { return self.raise_error_kind("TypeError", &msg); } // sloppy 函数:own caller/arguments 数据属性(ES5 遗留,node 行为)—— // caller = 调用方函数(帧栈次顶帧 callee),arguments = 本对象。strict // 函数无 own caller/arguments,经原型链解析到 FP 受限访问器(访问即抛)。 if !strict && callee.is_object() { let caller = self .frames .get(self.frames.len().saturating_sub(2)) .map(|f| f.callee) .unwrap_or(JsValue::undefined()); let callee_obj = unsafe { &mut *callee.as_js_object_ptr() }; let caller_si = self.kernel_core.perm_interner().intern("caller").0; let arguments_si = self.kernel_core.perm_interner().intern("arguments").0; let data_attrs = PropAttributes::new(true, false, true); if let Err(msg) = self.define_data_property(callee_obj, caller_si, caller, data_attrs) { return self.raise_error_kind("TypeError", &msg); } if let Err(msg) = self.define_data_property(callee_obj, arguments_si, JsValue::from_js_object(obj_ptr), data_attrs) { return self.raise_error_kind("TypeError", &msg); } } // @@iterator:与 Array.prototype[Symbol.iterator] 共享同一函数对象, // 使 arguments 对象可迭代(可写、不可枚举、可配置)。 let sym_iter_si = encode_symbol_key(self.realm_id(), 0); // SAFETY: array_proto 是 perm 层内置对象(BuiltinWorld 持有),地址稳定且 // 跨 epoch/session 存活,本调用期间无 GC 搬移。 let array_proto = self.realm.session.borrow().builtin_world().array_proto.as_ptr(); let iter_val = match self.ordinary_get(unsafe { &*array_proto }, sym_iter_si, JsValue::undefined()) { Ok(v) => v, Err(msg) => return self.raise_error_kind("TypeError", &msg), }; if let Err(msg) = self.define_data_property(obj, sym_iter_si, iter_val, PropAttributes::new(true, false, true)) { return self.raise_error_kind("TypeError", &msg); } self.regs[rd] = JsValue::from_js_object(obj_ptr); Ok(()) } /// 创建 rest 参数数组:实参区下标 ≥ `fixed_count` 的实参收集为数组存入 `rd`。 /// /// # 步骤 /// 1. 读当前帧(或 inline 同步调用)的实参区基址与个数。 /// 2. 实参个数超出固定形参数的部分作为数组元素写入。 /// /// # 边界与前提 /// - 实参来源与 CREATE_ARGUMENTS 相同(统一压帧入口 `push_bytecode_frame` 写入 spill 栈)。 /// - 实参不足 `fixed_count` 时数组为空。 pub(crate) fn dispatch_create_rest_array(&mut self, rd: usize, fixed_count: usize) -> Result<(), String> { let (base, count) = match self.frames.last() { Some(frame) => (frame.arguments_base, frame.arguments_count), None => (self.inline_args_base, self.inline_args_count), }; let n = count as usize; let rest_len = n.saturating_sub(fixed_count); let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let obj_ptr = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), rest_len)); let obj = unsafe { &mut *obj_ptr }; for i in 0..rest_len { let val = self .spill_stack .get(base as usize + fixed_count + i) .copied() .unwrap_or(JsValue::undefined()); obj.set_prop_at(i as u32, val); } self.regs[rd] = JsValue::object(obj_ptr as *mut u8); Ok(()) } #[inline(always)] pub(crate) fn dispatch_new_array(&mut self, rd: usize, instr: u32) { let n = opcode::imm16(instr) as usize; vm_trace!("NEW_ARRAY rd={} n={}", rd, n); let proto_ptr = self.realm.session.borrow().builtin_world().array_proto.as_ptr() as *mut JsObject; let obj = self.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr), n)); self.regs[rd] = JsValue::object(obj as *mut u8); } #[inline(always)] pub(crate) fn dispatch_void(&mut self, rd: usize) { vm_trace!("VOID rd={}", rd); self.regs[rd] = JsValue::undefined(); } }