//! 属性键推导与解析:JsValue 转属性键 si(整数键区间/字符串键/Symbol 键)、 //! 数组下标反解、非对象基删除与属性解析及自身属性槽查找。 use oxide_runtime_api as coercion; use oxide_types::object::JsObject; use oxide_types::private_key::{encode_symbol_key, int_key_value, is_int_key, make_int_key, INT_KEY_COUNT}; use oxide_types::value::JsValue; use super::{canonical_index_of, canonical_index_units, Vm, MAX_PROTO_CHAIN_DEPTH}; use crate::vm_trace; impl Vm { /// 把符号原语的局部下标编码为属性键:经 realm 感知编码(realm 编号占高位、 /// 局部下标占低 20 位),realm 0 路径与旧编码逐字节一致。 fn symbol_value_key(&self, idx: u32) -> u32 { encode_symbol_key(self.realm_id(), idx) } /// 把 `JsValue` 转为属性键 si(字符串 intern id,`u32`)。 /// /// 非负小整数(含整值 double)直接编码到整数键区间,免 to_string + intern; /// 字符串中形如数组下标的规范数字串(`"5"`)映射到同一整数键,保证 /// `obj["5"]` 与 `obj[5]` 键等价。 /// /// # 边界与前提 /// - 索引超 `INT_KEY_COUNT`(2^30)时回退字符串键路径(intern + 反查仍正确) /// - 负数与小数不进入整数键区间(`arr[-1]`/`arr[1.5]` 是普通字符串键) pub(crate) fn property_key_si(&mut self, val: JsValue) -> Result { if val.is_int() { let i = val.as_int(); if i >= 0 && (i as u32) < INT_KEY_COUNT { return Ok(make_int_key(i as u32)); } } else if val.is_double() { let d = val.as_double(); if d >= 0.0 && d.fract() == 0.0 && d < INT_KEY_COUNT as f64 { return Ok(make_int_key(d as u32)); } } else if val.is_string() { // SAFETY: val 是字符串值,按载荷形态桥接为永久 key id。 let s = unsafe { &*val.as_string_ptr() }; if s.is_flat() { // Flat:良形 UTF-8 文本,走与单元路径同口径的编码入键空间。 return Ok(self.string_key_text(s.as_str())); } // 单元载荷:键推导同规范(见 string_key_units)。 return Ok(self.string_key_units(&s.units())); } // Symbol 值直接编码为 Symbol 键(不进字符串 interner,键相互独立); // realm 编号占高位、局部下标占低 20 位,realm 0 与旧编码逐字节一致。 if val.is_symbol() { return Ok(self.symbol_value_key(val.as_symbol_local_index())); } if val.is_object() { // ToPropertyKey:对象经 ToPrimitive(string hint),结果为 Symbol 时直接作键; // 其余字符串按单元序列推导键(避免 lossy 文本桥接破坏孤立 surrogate 键)。 let prim = coercion::to_primitive(val, coercion::ToPrimitiveHint::String, self)?; if prim.is_symbol() { return Ok(self.symbol_value_key(prim.as_symbol_local_index())); } let units = oxide_runtime_api::to_units_full(prim, self)?; return Ok(self.string_key_units(&units)); } // 其它原始值(BigInt 等):ToPropertyKey 一律转字符串并走规范化,避免与 // 数字键区间分裂(`o[5n]` 与 `o["5"]`/`o[5]` 必须同键)。 let units = oxide_runtime_api::to_units_full(val, self)?; Ok(self.string_key_units(&units)) } /// 从良形文本串推导属性键 si(`property_key_si` Flat 分支与 `string_key_si` /// 条目的共享入口):规范数组下标 → 整数键,其余以 `encode_key` 形态入键空间 /// ——无 FFFD 的良形文本编码恒等,含 FFFD 的键与单元路径(Cons 载荷、 /// `string_key_si`)收敛到同一编码形态,同一逻辑键不因构造路径分裂。 pub(crate) fn string_key_text(&self, text: &str) -> u32 { if let Some(i) = canonical_index_of(text) { return make_int_key(i); } if text.contains('\u{FFFD}') { let units: Vec = text.encode_utf16().collect(); let key = oxide_kernel::string_forge::encode_key(&units); self.kernel_core.perm_interner().intern(&key).0 } else { self.kernel_core.perm_interner().intern(text).0 } } /// 从单元序列推导属性键 si(`property_key_si` 字符串分支的口径抽取): /// 规范数组下标 → 整数键,其余以 `encode_key` 形态入键空间(孤立 surrogate /// 以转义形态区分,不与良形键碰撞)。 pub(crate) fn string_key_units(&self, units: &[u16]) -> u32 { if let Some(i) = canonical_index_units(units) { make_int_key(i) } else { let key = oxide_kernel::string_forge::encode_key(units); self.kernel_core.perm_interner().intern(&key).0 } } /// 属性键反解数组下标:整数键直取,字符串键须无前导零且 `u32` 解析成功。 pub(crate) fn array_index_from_property_key(&self, prop_name_si: u32) -> Option { if is_int_key(prop_name_si) { return Some(int_key_value(prop_name_si)); } let key = self.kernel_core.perm_interner().lookup(prop_name_si)?; if key.is_empty() || (key.len() > 1 && key.starts_with('0')) { return None; } key.parse::().ok() } /// 删除属性引用基非对象面(delete 成员表达式的 ToObject 基求值): /// null/undefined 基 ToObject 抛 TypeError(规范 12.5.3.2 步 5.b);其余原始 /// 基装箱后按自身属性集判删除成败——字符串装箱体自身属性恰为规范下标 /// 0..len-1 与 "length"(均不可配置,删除失败返回 false),其它键与其它 /// 原始装箱体(number/boolean/BigInt/Symbol 无自身属性)删除成功返回 /// true。 /// /// # 步骤 /// 1. null/undefined 基抛 TypeError 并早返回(异常已展开,不再写结果槽) /// 2. 字符串基按下标键区间判自身属性;非字符串基恒真 /// 3. 严格模式下删除失败(字符串下标/length 不可配置)抛 TypeError /// 4. 布尔结果写 `rd` 槽(与对象基臂同槽位) /// /// # 边界与前提 /// - 调用点已证基非对象;对象基走既有对象臂,不进本函数 /// - 字符串长度口径按 UTF-16 码元(与字符串自身属性面一致);非规范数字串 /// 键(前导零等)与 symbol 键走"无自身属性"面 /// /// # 副作用 /// - 抛错路径经 `unwind` 改写 pc / 异常通道;成功路径写 `regs[rd]` pub(crate) fn delete_prop_non_object_base( &mut self, base: JsValue, rd: usize, key_si: u32, ) -> Result { if base.is_null() || base.is_undefined() { self.raise_type_error("delete on non-object")?; return Ok(true); } let deleted = if base.is_string() { let len = unsafe { (*base.as_string_ptr()).units().len() }; match self.array_index_from_property_key(key_si) { Some(i) if (i as usize) < len => false, Some(_) => true, None => key_si != self.length_si, } } else { true }; // 删除失败即命中不可配置自身属性,严格模式下 delete 运算符须抛 TypeError。 if !deleted && self.current_strict() { self.raise_type_error("Cannot delete property")?; return Ok(true); } self.regs[rd] = JsValue::bool(deleted); Ok(false) } /// 属性读值解析:按数组 length 虚拟属性、元素区、shape 槽、原型链的顺序 /// 查找,命中即返回,全链 miss 返回 `None`。 /// /// 数组元素区的 hole(删除标记)视同不存在;原型链查找止于 /// `MAX_PROTO_CHAIN_DEPTH` 深度上限。 pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option { vm_trace!("resolve_property: shape_id={} prop_name_si={}", obj.shape_id(), prop_name_si); let length_si = self.length_si; if obj.is_array() && prop_name_si == length_si { return Some(obj.logical_len_value()); } if obj.is_array() { if let Some(index) = self.array_index_from_property_key(prop_name_si) { // 数组元素区:hole(删除标记)视为不存在。 if index < obj.array_prop_count && !obj.prop_meta_at(index).is_some_and(|m| m.is_hole()) { return Some(obj.get_prop_at(index)); } } } if let Some(pos) = self.kernel_core.shape_forge().lookup_position(obj.shape_id(), prop_name_si) { let val = obj.get_prop_at(pos); if !val.is_undefined() || obj.prop_vec_len() > pos as usize { return Some(val); } } 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 .kernel_core .shape_forge() .lookup_position(proto_obj.shape_id(), prop_name_si) { let val = proto_obj.get_prop_at(pos); if !val.is_undefined() || proto_obj.prop_vec_len() > pos as usize { return Some(val); } } proto = proto_obj.proto(); } None } /// 存在性判定(规范 HasProperty):自身与原型链逐级检查,任一层 P 为自有 /// 属性即存在。数组 length 虚拟属性恒存在;数组元素区 hole 视同缺失; /// 其余按 shape 槽判定。 /// /// TypedArray 经统一数值键门(exotic [[HasProperty]]):界内整数索引 /// 判存在;数字无效键(负/分数/±Infinity/NaN/越界,含 "-0" 特例)立即 /// false,不查自身命名属性也不走原型链;非规范数字串落普通路径。原型链 /// 上的 TA 按同口径经门判定。 /// /// 与 `resolve_property` 的差异:原型链上每层都检查数组元素区 /// (`resolve_property` 只在顶层检查元素区,链上仅走 shape 槽), /// 继承自父数组的索引属性在此判存在。 pub(crate) fn has_property(&self, obj: &JsObject, prop_name_si: u32) -> bool { let length_si = self.length_si; let mut current = Some(obj); let mut depth = 0usize; while let Some(obj) = current { if obj.is_array() && prop_name_si == length_si { return true; } // 统一数值键门(exotic [[HasProperty]]):同 `ordinary_get_inner` // 的口径,链上每层同口径判定。 if obj.is_typed_array_obj() { match oxide_builtins::typed_array::ta_index_gate(self, obj, prop_name_si) { oxide_builtins::typed_array::TaIndexGate::NumericValid(_) => return true, oxide_builtins::typed_array::TaIndexGate::NumericInvalid => return false, oxide_builtins::typed_array::TaIndexGate::Ordinary => {} } } if self.get_own_property_slot(obj, prop_name_si).is_some() { return true; } if depth >= MAX_PROTO_CHAIN_DEPTH { break; } depth += 1; let proto = obj.proto(); current = proto.is_object().then(|| unsafe { &*proto.as_js_object_ptr() }); } false } /// 查找自身属性槽下标:数组 length 虚拟属性返回 `None`,元素区返回下标 /// (hole 视缺失),shape 槽命中即属性在场并返回存储下标(数组加元素区 /// 偏移,普通对象槽位即下标);值可为显式 undefined,不参与存在性判定; /// 未命中返回 `None`。 pub(crate) fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option { let length_si = self.length_si; if obj.is_array() && prop_name_si == length_si { return None; } if obj.is_array() { if let Some(index) = self.array_index_from_property_key(prop_name_si) { // 数组元素区:hole(删除标记)视为不存在。 if index < obj.array_prop_count && !obj.prop_meta_at(index).is_some_and(|m| m.is_hole()) { return Some(index); } } } self.kernel_core .shape_forge() .lookup_position(obj.shape_id(), prop_name_si) .and_then(|pos| { if obj.is_array() { // 数组属性存储索引 = array_prop_count + shape 槽位(与元素区分)。 // shape 槽命中即属性在场:删除重建保证槽随属性移除, // 显式 undefined 值同样在场(规范存在性只看自身槽)。 Some(obj.array_prop_count + pos) } else { let val = obj.get_prop_at(pos); if !val.is_undefined() || obj.prop_vec_len() > pos as usize { Some(pos) } else { None } } }) } }