use crate::{ic_debug, ic_trace, vm_trace}; use oxide_bytecode::opcode::{OpCode, IC_EXT_WORDS}; use oxide_runtime_api as coercion; use oxide_types::object::JsObject; use oxide_types::private_key::{int_key_value, is_int_key, make_int_key, make_private_name_id}; use oxide_types::value::JsValue; use crate::ic_helper::{self, ic_get_hit, ic_set_hit, VIRTUAL_LENGTH_SLOT}; use crate::vm::{Vm, MAX_PROTO_CHAIN_DEPTH}; use crate::vm_props::SetContract; #[cold] fn prop_cache_miss() {} impl Vm { pub(crate) fn dispatch_property_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("PROPERTY_OP {:?} rd={} a={} b={}", op, rd, a, b); match op { OpCode::IC_GET_PROP => self.dispatch_ic_get_prop(a, b), OpCode::IC_SET_PROP => self.dispatch_ic_set_prop(rd, a, b), OpCode::GET_PROP => self.dispatch_get_prop(rd, a, b), OpCode::SET_PROP => self.dispatch_set_prop(rd, a, b), OpCode::SET_PROP_BATCH => self.dispatch_set_prop_batch(rd, a, b), OpCode::GET_PROP_DYNAMIC => self.dispatch_get_prop_dynamic(rd, a, b), OpCode::SET_PROP_DYNAMIC => self.dispatch_set_prop_dynamic(rd, a, b), OpCode::SET_ELEM => self.dispatch_set_elem(rd, a, b), OpCode::GET_PRIVATE => self.dispatch_get_private(rd, a, b), OpCode::SET_PRIVATE => self.dispatch_set_private(rd, a, b), OpCode::INIT_PRIVATE => self.dispatch_init_private(rd, a, b), OpCode::PRIVATE_BRAND_IN => self.dispatch_private_brand_in(rd, a, b), _ => unreachable!("non-property opcode passed to dispatch_property_op"), } } /// SET 路径的接收者解析:对象直取;非对象按严格性分流——strict 抛 /// TypeError(规范 OrdinarySetWithOwnDescriptor 的 ToObject 基语义在 /// strict 下不 auto-box),sloppy 经 ToObject 自动装箱(null/undefined /// 抛 TypeError,其余原始值写盒后丢弃盒)。 fn set_prop_base(&mut self, receiver: JsValue) -> Result, String> { if receiver.is_object() { return self.checked_object_ptr(receiver, "Cannot create property on non-object"); } if self.current_strict() { self.raise_type_error("Cannot create property on a primitive value")?; return Ok(None); } // null/undefined:ToObject 抛可捕获 TypeError(经 raise 走 JS 异常, // 非 Rust Err 传播,try/catch 可接)。 if receiver.is_null() || receiver.is_undefined() { self.raise_type_error("Cannot convert null or undefined to object")?; return Ok(None); } let boxed = coercion::to_object(receiver, self)?; Ok(Some(boxed.as_js_object_ptr())) } fn private_key_from_reg(&self, reg: usize) -> u32 { let value = self.regs[reg]; let local_id = if value.is_int() { value.as_int().max(0) as u32 } else if value.is_double() { value.as_double().max(0.0) as u32 } else { 0 }; make_private_name_id(local_id) } fn primitive_property_get(&mut self, val: JsValue, prop_name_si: u32) -> Result, String> { if val.is_string() { let length_si = self.length_si; if prop_name_si == length_si { // SAFETY: val 是字符串值。utf16_len 构造时缓存,O(1) 读取。 let len = unsafe { (*val.as_string_ptr()).utf16_len() }; return Ok(Some(JsValue::int(len as i32))); } // 整数索引键:规范 String exotic [[Get]] 界内返码元子串(码元口径非码点), // 越界返 undefined——必须 Some(undefined) 而非 None,None 会被调用点当 // "非原始值属性"回落对象臂抛 TypeError。 if is_int_key(prop_name_si) { // SAFETY: val 是字符串值。 let s = unsafe { &*val.as_string_ptr() }; let len = s.utf16_len(); let idx = int_key_value(prop_name_si); if idx < len { // 码元读取:ASCII 命中共享 perm 串零分配,非 ASCII 物化 1 单元会话串。 let units = s.units(); return Ok(Some(self.unit_char_value(units[idx as usize]))); } return Ok(Some(JsValue::undefined())); } let proto_ptr = self.realm.session.borrow().builtin_world().string_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_int() || val.is_double() { let proto_ptr = self.realm.session.borrow().builtin_world().number_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_bool() { let proto_ptr = self.realm.session.borrow().builtin_world().boolean_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_bigint() { let proto_ptr = self.realm.session.borrow().builtin_world().bigint_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } if val.is_symbol() { let proto_ptr = self.realm.session.borrow().builtin_world().symbol_proto.as_ptr() as *mut JsObject; let proto = unsafe { &*proto_ptr }; return self.ordinary_get(proto, prop_name_si, val).map(Some); } Ok(None) } fn private_slot(&self, obj: &JsObject, private_key: u32) -> Option { self.kernel_core.shape_forge().lookup_position(obj.shape_id(), private_key) } /// 沿实例→proto 链查找私有方法/访问器槽(存于类的 home=proto)。字段槽不跨原型 /// (PrivateFieldFind 只查 own)——链上遇到字段槽(无 hole meta)即拒绝,防 /// `Object.create(instance)` 穿透读取实例字段。 fn find_private_method_slot(&self, obj: &JsObject, private_key: u32) -> Option<*mut JsObject> { let mut proto = obj.proto(); let mut depth = 0usize; while proto.is_object() && depth < MAX_PROTO_CHAIN_DEPTH { depth += 1; let proto_obj = unsafe { &*proto.as_js_object_ptr() }; if let Some(pos) = self.private_slot(proto_obj, private_key) { // 方法/访问器槽有 hole 标记(私有方法)或 is_accessor 标记(私有 getter/setter); // 字段槽(存实例)沿链出现说明接收者并非本类实例 → 拒绝穿透。 if proto_obj.prop_meta_at(pos).is_some_and(|m| m.is_hole() || m.is_accessor) { return Some(proto_obj as *const JsObject as *mut JsObject); } return None; } proto = proto_obj.proto(); } None } /// 私有方法/访问器/静态字段访问的 brand 检查:接收者 own 的 brand 槽(构造时写入 /// 的类 brand 对象)必须与当前类 brand 对象同一。类每次求值创建新 brand,同字节码 /// 多次实例化(eval/factory)可借此区分。 fn private_brand_check(&mut self, obj: &JsObject, brand_reg: usize, brand_key: u32) -> Result<(), String> { let Some(pos) = self.private_slot(obj, brand_key) else { return self.raise_type_error("private field brand check failed"); }; let inst_brand = obj.get_prop_at(pos); if !coercion::strict_equality(inst_brand, self.regs[brand_reg]) { return self.raise_type_error("private field brand check failed"); } Ok(()) } fn dispatch_get_private(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("GET_PRIVATE rd={} a={} b={}", rd, a, b); let obj_val = self.regs[a]; let Some(obj_ptr) = self.checked_object_ptr(obj_val, "private field access on non-object")? else { return Ok(()); }; let private_key = self.private_key_from_reg(b); let brand_reg = self.bytecode[self.pc] as usize; let brand_key = make_private_name_id(self.bytecode[self.pc + 1]); self.pc += 2; let obj = unsafe { &*obj_ptr }; // 字段路径:own 有该私有槽(字段存实例 own)→ 直接读,不跨原型、不做 brand 值比较 // (PrivateFieldFind 只查 own;接收者带槽即构造器产物)。 if let Some(pos) = self.private_slot(obj, private_key) { if let Some(meta) = obj.prop_meta_at(pos) { if meta.is_accessor { if meta.get.is_undefined() { return self.raise_type_error("private field has no getter"); } let getter = meta.get; self.push_bytecode_getter_frame(getter, obj_val, rd as u8)?; self.accessor_frame_target_reg.take(); return Ok(()); } } self.regs[rd] = obj.get_prop_at(pos); return Ok(()); } // 方法路径:own 无槽(私有方法/访问器存于 home=proto)→ 沿链找方法槽 + brand 检查。 let Some(home_ptr) = self.find_private_method_slot(obj, private_key) else { return self.raise_type_error("private field brand check failed"); }; if brand_reg != 0 { self.private_brand_check(obj, brand_reg, brand_key)?; } let home = unsafe { &*home_ptr }; let pos = self.private_slot(home, private_key).expect("slot just found"); if let Some(meta) = home.prop_meta_at(pos) { if meta.is_accessor { if meta.get.is_undefined() { return self.raise_type_error("private field has no getter"); } let getter = meta.get; self.push_bytecode_getter_frame(getter, obj_val, rd as u8)?; // 与 dispatch_get_prop 一致:getter 帧入栈后消费 accessor_frame_target_reg, // 避免残留状态污染后续属性访问(getter 返回经 continuation 写 rd)。 self.accessor_frame_target_reg.take(); return Ok(()); } } self.regs[rd] = home.get_prop_at(pos); Ok(()) } fn dispatch_set_private(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("SET_PRIVATE rd={} a={} b={}", rd, a, b); let obj_val = self.regs[rd]; let Some(obj_ptr) = self.checked_object_ptr(obj_val, "private field assignment on non-object")? else { return Ok(()); }; let private_key = self.private_key_from_reg(b); let brand_reg = self.bytecode[self.pc] as usize; let brand_key = make_private_name_id(self.bytecode[self.pc + 1]); self.pc += 2; let obj = unsafe { &*obj_ptr }; // 字段路径:own 有槽(字段存实例 own)→ 直接写,不跨原型、不做 brand 值比较。 if let Some(pos) = self.private_slot(obj, private_key) { if let Some(meta) = obj.prop_meta_at(pos) { if meta.is_accessor { if meta.set.is_undefined() { return self.raise_type_error("private field has no setter"); } let setter = meta.set; let value = self.regs[a]; return self.call_or_push_setter(setter, obj_val, value, true); } // 私有方法槽不可写(init 时打标记)。 if meta.is_hole() { return self .raise_type_error("Cannot write private member to an object whose class did not declare it"); } } let value = self.regs[a]; let obj = unsafe { &mut *obj_ptr }; obj.set_prop_shape(pos, value); return Ok(()); } // 方法路径:own 无槽(私有方法/访问器存于 home=proto)→ 沿链找方法槽 + brand 检查。 let Some(home_ptr) = self.find_private_method_slot(obj, private_key) else { return self.raise_type_error("private field brand check failed"); }; if brand_reg != 0 { self.private_brand_check(obj, brand_reg, brand_key)?; } let home = unsafe { &*home_ptr }; let pos = self.private_slot(home, private_key).expect("slot just found"); if let Some(meta) = home.prop_meta_at(pos) { if meta.is_accessor { if meta.set.is_undefined() { return self.raise_type_error("private field has no setter"); } let setter = meta.set; let value = self.regs[a]; return self.call_or_push_setter(setter, obj_val, value, true); } // 私有方法槽不可写(init 时打标记)。 if meta.is_hole() { return self .raise_type_error("Cannot write private member to an object whose class did not declare it"); } } let value = self.regs[a]; let home = unsafe { &mut *home_ptr }; home.set_prop_shape(pos, value); Ok(()) } fn dispatch_init_private(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("INIT_PRIVATE rd={} a={} b={}", rd, a, b); let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "private field initialization on non-object")? else { return Ok(()); }; let private_key = self.private_key_from_reg(b); let is_method = self.bytecode[self.pc] != 0; 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() .lookup_position(obj.shape_id(), private_key) .is_some() { // 私有元素重复初始化(构造器返回外部对象、同一对象再次初始化): // 规范 PrivateFieldAdd 对已存在的私有名抛 TypeError。 return self.raise_type_error("Cannot initialize private element twice"); } self.set_or_create_prop_value(obj, private_key, value); if is_method { let pos = self .kernel_core .shape_forge() .lookup_position(obj.shape_id(), private_key) .expect("private slot just created"); obj.set_private_method_meta(pos); } Ok(()) } fn dispatch_private_brand_in(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("PRIVATE_BRAND_IN rd={} a={} b={}", rd, a, b); let obj_val = self.regs[a]; if !obj_val.is_object() { // `#x in rval`:rval 非对象抛 TypeError(规范 PrivateFieldIn 步骤 4)。 return self.raise_type_error("private field `in` requires an object on the right-hand side"); } let obj = unsafe { &*obj_val.as_js_object_ptr() }; let private_key = self.private_key_from_reg(b); let brand_reg = self.bytecode[self.pc] as usize; let brand_key = make_private_name_id(self.bytecode[self.pc + 1]); self.pc += 2; // 只查 own 私有元素(PrivateFieldIn 不跨原型链):own 有该私有槽(字段)→ true; // 否则接收者 own 有当前类 brand 槽(方法/访问器,槽在 home)→ true;否则 false。 // brand_key 是类级私有名 id(每类独立),own 存在该槽即本类实例——存在性即可 // 判定,不做 cell 值比较(规避 upvalue cell 生命周期缺陷)。 if self.private_slot(obj, private_key).is_some() { self.regs[rd] = JsValue::bool(true); return Ok(()); } let has_brand = brand_reg != 0 && self.private_slot(obj, brand_key).is_some(); self.regs[rd] = JsValue::bool(has_brand); Ok(()) } fn dispatch_ic_get_prop(&mut self, a: usize, b: usize) -> Result<(), String> { let val = self.regs[a]; let obj_ptr = val.as_object_ptr() as *mut JsObject; if obj_ptr.is_null() { // 入口快判:键寄存器恒为 perm 串,指针相等即键为 "length"; // 字符串接收者直返码元长度,免键解析(hash64 + DashMap + RwLock)。 let key_val = self.regs[b]; if key_val.is_string() && key_val.as_string_ptr() == self.length_perm_ptr && val.is_string() { // SAFETY: val 是字符串值。 let len = unsafe { (*val.as_string_ptr()).utf16_len() }; self.regs[a] = JsValue::int(len as i32); self.pc += IC_EXT_WORDS; return Ok(()); } let prop_name_si = self.property_key_si(self.regs[b])?; if let Some(resolved) = self.primitive_property_get(val, prop_name_si)? { self.regs[a] = resolved; self.pc += IC_EXT_WORDS; return Ok(()); } self.raise_type_error("IC_GET_PROP on non-object")?; return Ok(()); } let addr = obj_ptr as usize; if addr < 0x10000 || addr % std::mem::align_of::() != 0 { self.raise_type_error("IC_GET_PROP on non-object")?; return Ok(()); } let obj = unsafe { &*obj_ptr }; // 入口快判:键寄存器恒为 perm 串,指针相等即键为 "length"; // 数组接收者直返逻辑长度,免 IC 读与键解析(与虚拟长度槽口径一致)。 let key_val = self.regs[b]; if key_val.is_string() && key_val.as_string_ptr() == self.length_perm_ptr && obj.is_array() { self.regs[a] = obj.logical_len_value(); self.pc += IC_EXT_WORDS; return Ok(()); } let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_slot0(&self.bytecode, &mut self.pc); let ic_pc = self.pc; if obj.has_prop_meta() { // meta 对象整对象走慢路径,此时才解析键。 let prop_name_si = self.property_key_si(self.regs[b])?; let val = self.ordinary_get_with_target(obj, prop_name_si, val, a as u8)?; if self.accessor_frame_target_reg.take().is_none() { self.regs[a] = val; } return Ok(()); } if let Some(value) = ic_get_hit(obj, cached_shape_id, cached_slot, cached_depth) { // 槽 0 单态命中:只比对 shape_id,不解析键。 self.regs[a] = value; self.profiling.record_ic_hit(); ic_trace!("IC_GET hit shape={} slot={} depth={}", cached_shape_id, cached_slot, cached_depth); } else if let Some(value) = ic_helper::ic_get_hit_poly(obj, &self.bytecode, ic_pc - IC_EXT_WORDS) { // 多态槽(1..3)命中。 self.regs[a] = value; self.profiling.record_ic_hit(); ic_trace!("IC_GET poly hit pc={} shape={}", ic_pc, obj.shape_id()); } else { // miss 分支才解析键(IC 站点键恒为编译期字符串常量,延后解析无 ToPrimitive 副作用)。 let prop_name_si = self.property_key_si(self.regs[b])?; self.profiling.record_ic_miss(); prop_cache_miss(); // 数组 length 虚拟槽:Array.prototype 无 own length,写回恒落空; // 直接学入标记并读逻辑长度,跳过模板查 / ordinary_get / proto 空跑。 if obj.is_array() && prop_name_si == self.length_si { ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), VIRTUAL_LENGTH_SLOT, 0); ic_debug!("IC_GET virtual length hit shape={}", obj.shape_id()); self.regs[a] = obj.logical_len_value(); return Ok(()); } // 模板快路径:prop_forge 只缓存每 shape 最后新增的属性,恰好覆盖该属性时直取槽位。 if let Some(template) = self.kernel_core.prop_forge().get_template(obj.shape_id()) { if template.prop_name == prop_name_si && template.position < obj.prop_vec_len() as u32 { ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), template.position, 0); ic_debug!( "IC_GET propforge hit shape={} prop={} slot={}", obj.shape_id(), prop_name_si, template.position ); self.regs[a] = obj.get_prop_shape(template.position); } else { // 模板不覆盖该属性(读非末位属性):走通用 miss 路径并写回, // 否则该 shape 的缓存条目永远学不到。 ic_debug!("IC_GET miss shape={} prop={}", obj.shape_id(), prop_name_si); self.regs[a] = self.proto_chain_ic_get(obj, prop_name_si, val)?; } } else { ic_debug!("IC_GET miss shape={} prop={}", obj.shape_id(), prop_name_si); let resolved = self.ordinary_get(obj, prop_name_si, val)?; // 先试自身属性(快路径,depth=0)。 if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { if !obj.is_accessor_meta(pos) { ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0); ic_debug!("IC_GET write-back own shape={} slot={}", obj.shape_id(), pos); } } else { // 沿原型链查找真正拥有该属性的对象。 let mut cursor = obj.proto().as_js_object_ptr(); let mut depth = 1u8; while !cursor.is_null() { let co_ref = unsafe { &*cursor }; if let Some(pos) = self.kernel_core.shape_forge().lookup_position(co_ref.shape_id(), prop_name_si) { if !co_ref.is_accessor_meta(pos) { ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, co_ref.shape_id(), pos, depth); ic_debug!( "IC_GET write-back proto shape={} slot={} depth={}", co_ref.shape_id(), pos, depth ); } break; } if !co_ref.proto().is_object() { break; } cursor = co_ref.proto().as_js_object_ptr(); depth += 1; } } self.regs[a] = resolved; } } Ok(()) } fn dispatch_ic_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { let Some(obj_ptr) = self.set_prop_base(self.regs[rd])? else { return Ok(()); }; // read_ic_slot0 先消费全部扩展字,命中路径无需解析键。 let (cached_shape_id, cached_slot, cached_depth) = ic_helper::read_ic_slot0(&self.bytecode, &mut self.pc); let ic_pc = self.pc; let value = self.regs[a]; let receiver = self.regs[rd]; let obj = unsafe { &mut *obj_ptr }; // 命中路径零键零拦截:__proto__ 赋值从不写 IC,缓存槽必为数据槽。 if !obj.has_prop_meta() && (ic_set_hit(obj, cached_shape_id, cached_slot, cached_depth, value) || ic_helper::ic_set_hit_poly(obj, &self.bytecode, ic_pc - IC_EXT_WORDS, value)) { self.profiling.record_ic_hit(); ic_trace!("IC_SET hit shape={} slot={} depth={}", cached_shape_id, cached_slot, cached_depth); // IC 命中快路径防御:今日全局对象带 meta 不可达,防 meta 不变量 // 被未来优化破坏后镜像静默失步(键由 (shape, slot) 反查)。 self.sync_global_builtin_mirror_slot(obj, cached_shape_id, cached_slot, cached_depth, value); return Ok(()); } // 慢路径(has_prop_meta / miss)才解析键并拦截 __proto__ 赋值,须先于写槽, // 防 __proto__ 落入数据槽。 let prop_name_si = self.property_key_si(self.regs[b])?; if self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") { if self.is_object_prototype(obj_ptr) && !value.is_null() { self.raise_type_error("Object.prototype.__proto__ is immutable")?; return Ok(()); } obj.set_proto(value).map_err(|e| e.to_string())?; return Ok(()); } if obj.has_prop_meta() { self.ordinary_set_dispatch(obj, prop_name_si, value, receiver, self.current_strict())?; return Ok(()); } if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { self.profiling.record_ic_miss(); prop_cache_miss(); obj.set_prop_shape(pos, value); self.sync_global_builtin_mirror(obj, prop_name_si, value); ic_helper::write_ic_back(self.bytecode_mut(), ic_pc, obj.shape_id(), pos, 0); ic_debug!("IC_SET write-back shape={} slot={}", obj.shape_id(), pos); } else { self.profiling.record_ic_miss(); prop_cache_miss(); // 写新属性(shape 转换):数组 length / 整数索引键仍走完整 // ordinary_set_dispatch 语义,其余走 CreateDataProperty 快路径。 if self.named_prop_create_needs_ordinary_set(obj, prop_name_si) { self.ordinary_set_dispatch(obj, prop_name_si, value, receiver, self.current_strict())?; return Ok(()); } self.create_named_prop_fast( obj, prop_name_si, value, receiver, ic_pc, SetContract::dispatch(self.current_strict()), )?; } Ok(()) } fn dispatch_get_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("GET_PROP rd={} a={} b={}", rd, a, b); let prop_name_si = self.property_key_si(self.regs[b])?; if let Some(value) = self.primitive_property_get(self.regs[rd], prop_name_si)? { self.regs[a] = value; return Ok(()); } let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "GET_PROP on non-object")? else { return Ok(()); }; let obj = unsafe { &*obj_ptr }; let val = self.ordinary_get_with_target(obj, prop_name_si, self.regs[rd], a as u8)?; if self.accessor_frame_target_reg.take().is_none() { self.regs[a] = val; } Ok(()) } fn dispatch_set_prop(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("SET_PROP rd={} a={} b={}", rd, a, b); let Some(obj_ptr) = self.set_prop_base(self.regs[rd])? else { return Ok(()); }; let prop_name_si = self.property_key_si(self.regs[b])?; if self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") { let proto_value = self.regs[a]; if self.is_object_prototype(obj_ptr) && !proto_value.is_null() { self.raise_type_error("Object.prototype.__proto__ is immutable")?; return Ok(()); } let obj = unsafe { &mut *obj_ptr }; obj.set_proto(proto_value).map_err(|e| e.to_string())?; return Ok(()); } let value = self.regs[a]; let obj = unsafe { &mut *obj_ptr }; self.ordinary_set_dispatch(obj, prop_name_si, value, self.regs[rd], self.current_strict())?; Ok(()) } /// 对象字面量批量构造的纯槽写:把值写入键序预建 shape 的 `slot` 槽。 /// 不做键解析、`__proto__` 拦截或任何形状变更——键与槽位由 /// NEW_OBJECT 的键常量表一次性建好。 /// /// # 边界与前提 /// - `slot` 必须 < NEW_OBJECT 预分配的槽数(emit 保证 ≤255)。 fn dispatch_set_prop_batch(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("SET_PROP_BATCH rd={} slot={}", rd, b); let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else { return Ok(()); }; let value = self.regs[a]; let obj = unsafe { &mut *obj_ptr }; obj.set_prop_storage(b, value); Ok(()) } fn dispatch_get_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("GET_PROP_DYNAMIC rd={} a={} b={}", rd, a, b); let prop_name_si = self.property_key_si(self.regs[a])?; let receiver = self.regs[rd]; // 数组元素 fast path:整数键 + 真数组 + 无元素 meta(无 hole/accessor/描述符) // + 界内 → 直读元素区,免 primitive_property_get / checked_object_ptr / // ordinary_get 调用链。判定条件与 ordinary_get_inner 数组分支严格等价。 // 非对象 receiver(如 `"abc"[0]`)须先 is_object 快检再 checked_object_ptr, // 否则该检查会误抛 TypeError(原路径对原始值走 primitive_property_get 不抛)。 if is_int_key(prop_name_si) && receiver.is_object() { if let Some(obj_ptr) = self.checked_object_ptr(receiver, "GET_PROP_DYNAMIC on non-object")? { let obj = unsafe { &*obj_ptr }; if obj.is_array() && obj.array_elements_meta_vec().is_none() { let idx = int_key_value(prop_name_si); if idx < obj.array_prop_count { self.regs[b] = obj.get_prop_at(idx); return Ok(()); } } } } if let Some(value) = self.primitive_property_get(receiver, prop_name_si)? { self.regs[b] = value; return Ok(()); } let Some(obj_ptr) = self.checked_object_ptr(receiver, "GET_PROP_DYNAMIC on non-object")? else { return Ok(()); }; let obj = unsafe { &*obj_ptr }; let val = self.ordinary_get_with_target(obj, prop_name_si, receiver, b as u8)?; if self.accessor_frame_target_reg.take().is_none() { self.regs[b] = val; } Ok(()) } fn dispatch_set_prop_dynamic(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("SET_PROP_DYNAMIC rd={} a={} b={}", rd, a, b); let Some(obj_ptr) = self.set_prop_base(self.regs[rd])? else { return Ok(()); }; let prop_name_si = self.property_key_si(self.regs[a])?; // 数组元素 fast path:整数键 + 真数组 + 无 meta + 界内 → 直写元素区, // 免 __proto__ 查询 / ordinary_set_dispatch / get_own_property_slot 调用链。 // 与 ordinary_set_inner 数组分支等价(界内写不触发 length 扩展、无 shadow // 同步)。越界写必须走原路径(CreateDataProperty + length 扩展),绝不可 // 直写——set_prop_storage 对越界下标会落到 hash_props 属性区。 if is_int_key(prop_name_si) { let obj = unsafe { &*obj_ptr }; if obj.is_array() && obj.array_elements_meta_vec().is_none() { let idx = int_key_value(prop_name_si); if idx < obj.array_prop_count { let value = self.regs[b]; let obj = unsafe { &mut *obj_ptr }; obj.set_prop_storage(idx as usize, value); return Ok(()); } } } // 整数键恒不可能等于 "__proto__"(字符串 intern id 远小于整数键区间), // 短路免去每次 set 的 RwLock 查询。 if !is_int_key(prop_name_si) && self.kernel_core.perm_interner().lookup(prop_name_si) == Some("__proto__") { let proto_value = self.regs[b]; if self.is_object_prototype(obj_ptr) && !proto_value.is_null() { self.raise_type_error("Object.prototype.__proto__ is immutable")?; return Ok(()); } let obj = unsafe { &mut *obj_ptr }; obj.set_proto(proto_value).map_err(|e| e.to_string())?; return Ok(()); } let value = self.regs[b]; let obj = unsafe { &mut *obj_ptr }; self.ordinary_set_dispatch(obj, prop_name_si, value, self.regs[rd], self.current_strict())?; Ok(()) } fn dispatch_set_elem(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> { vm_trace!("SET_ELEM rd={} a={} b={}", rd, a, b); let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "Cannot create property on non-object")? else { return Ok(()); }; let idx = if self.regs[a].is_int() { self.regs[a].as_int().max(0) as u32 } else if self.regs[a].is_double() { self.regs[a].as_double().max(0.0) as u32 } else { 0 }; let value = self.regs[b]; let obj = unsafe { &mut *obj_ptr }; // 有属性 meta(freeze/seal 逐属性写 meta 后恒 true,含 accessor 元素/描述符 // 元素):回落 ordinary_set 走完整 writable/accessor/extensible 检查,防止 // 直写绕过冻结语义。 if obj.has_prop_meta() { let si = make_int_key(idx); return self.ordinary_set_dispatch(obj, si, value, self.regs[rd], self.current_strict()); } // 无 meta 快路径:不可扩展对象禁止新增元素(越界下标);已有元素直写不受限 // (无 meta 即默认可写)。 if !obj.is_extensible() && idx >= obj.array_prop_count { self.raise_type_error("object is not extensible")?; return Ok(()); } // 越界索引写 length 不可写时失败(与 ordinary_set_inner 同序):SET_ELEM 当前 // 仅对 fresh 数组发射不可达,防未来 codegen 对非 fresh 数组发射时绕过守卫。 if idx >= obj.logical_len() && !obj.is_length_writable() { if self.current_strict() { self.raise_type_error("Cannot add property, array length is not writable")?; } return Ok(()); } obj.set_prop_at(idx, value); Ok(()) } } impl Vm { pub(crate) fn dispatch_delete_prop_static(&mut self, rd: usize) -> Result { vm_trace!("DELETE_PROP_STATIC rd={}", rd); let prop_idx = self.bytecode[self.pc] as usize; self.pc += 1; let key_val = self.immutables().get(prop_idx).copied().unwrap_or_else(JsValue::undefined); let prop_name_si = self.property_key_si(key_val)?; if !self.regs[rd].is_object() { // 非对象基:ToObject 基求值——null/undefined 抛 TypeError,其余原始 // 基装箱后按自身属性面判删除成败。 return self.delete_prop_non_object_base(self.regs[rd], rd, prop_name_si); } let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "delete on non-object")? else { return Ok(false); }; let obj = unsafe { &mut *obj_ptr }; // 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);严格模式分派见 // finish_member_delete。 let outcome = oxide_builtins::object::delete_own_property_outcome(self, obj, prop_name_si); 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 { vm_trace!("DELETE_PROP_DYNAMIC rd={}", rd); let prop_name_si = self.property_key_si(self.regs[b])?; if !self.regs[rd].is_object() { // 非对象基:ToObject 基求值——null/undefined 抛 TypeError,其余原始 // 基装箱后按自身属性面判删除成败。 return self.delete_prop_non_object_base(self.regs[rd], rd, prop_name_si); } let Some(obj_ptr) = self.checked_object_ptr(self.regs[rd], "delete on non-object")? else { return Ok(false); }; let obj = unsafe { &mut *obj_ptr }; // 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);严格模式分派见 // finish_member_delete。 let outcome = oxide_builtins::object::delete_own_property_outcome(self, obj, prop_name_si); match outcome { // deferred namespace 求值触发失败(cyclic / 失败缓存重抛)原值重抛。 Err(exc) => self.raise_captured(exc).map(|_| true), Ok(outcome) => self.finish_member_delete(rd, outcome), } } /// 成员形 delete 的结果分派:不可配置属性在严格模式抛 TypeError(规范要求 /// delete 运算符对 [[Delete]] 返回 false 的引用抛错),其余情形把「是否删除 /// 成功」写入 `rd`。 /// /// # 步骤 /// 1. 不可配置且当前上下文严格:抛 TypeError,返回 `Ok(true)` 令调用方重取 pc /// 2. 否则按三态投影布尔结果写 `rd`(`Missing`/`Deleted` 为 true) /// /// # 边界与前提 /// - 严格性取 `current_strict()`(覆盖帧栈顶 / inline 基线 / 顶层脚本三形态) /// /// # 副作用 /// - 抛错路径经 `unwind` 改写 pc / 异常通道;成功路径写 `regs[rd]` fn finish_member_delete( &mut self, rd: usize, outcome: oxide_builtins::object::DeleteOutcome, ) -> Result { if outcome == oxide_builtins::object::DeleteOutcome::NonConfigurable && self.current_strict() { self.raise_type_error("Cannot delete property")?; return Ok(true); } self.regs[rd] = JsValue::bool(outcome != oxide_builtins::object::DeleteOutcome::NonConfigurable); Ok(false) } /// 删除全局内置数据属性(delete 标识符):ext 字 = 键常量池下标(u16), /// `rd` = 布尔结果槽,`slot` = 内置镜像槽寄存器(0 = 无槽)。全局对象由 /// session 解析——函数体内 delete 不依赖 this 寄存器。 /// /// # 步骤 /// 1. 键常量 → intern si → 对全局对象走共享删除逻辑(不可配置 → false 且属性 /// 保留;缺失 → true;可配置 → 真删并返回 true) /// 2. 删除成功且 a 槽为有效镜像槽时槽写 undefined /// /// # 副作用 /// - 删除成功时修改全局对象 shape/属性表/generation /// - 删除成功时写镜像槽寄存器(该槽随后裸读回 undefined) /// /// # 边界与前提 /// - 键常量必须是字符串(emit 侧保证);非字符串按错误返回 /// - 镜像槽仅在 run/帧入口重载,不清槽则删除后裸读回入口预载的旧值—— /// 清槽与真删同生共死:删除失败(c:false)属性保留,槽值不动 pub(crate) fn dispatch_delete_global_prop_c(&mut self, rd: usize, slot: usize, key_idx: u16) -> Result<(), String> { vm_trace!("DELETE_GLOBAL_PROP_C slot={} idx={}", slot, key_idx); let idx = key_idx as usize; let key_val = self.immutables().get(idx).copied().unwrap_or(JsValue::undefined()); if !key_val.is_string() { return Err(format!("DELETE_GLOBAL_PROP_C constant index {idx} is not a string key")); } let si = self.property_key_si(key_val)?; // 全局词法环境绑定(顶层 let/const)不可删除:镜像命中恒 false 且属性保留。 { let session = self.realm.session.borrow(); if self.resolve_property(session.global_lexical(), si).is_some() { self.regs[rd] = JsValue::bool(false); return Ok(()); } } let global_ptr = self.realm.session.borrow().global_object().as_ptr() as *mut JsObject; // SAFETY: 全局对象钉在 session 永久区,指针在 VM 生命周期内有效。 let obj = unsafe { &mut *global_ptr }; // 统一走共享删除逻辑(与 Reflect.deleteProperty 同源);不可配置返回 // 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(); } self.regs[rd] = JsValue::bool(deleted); Ok(()) } }