feat: TA 定义自身属性数值索引臂与原型链门口径

This commit is contained in:
2026-09-23 08:31:47 +00:00
parent 0645a3d2e4
commit 18b80f2f41
7 changed files with 343 additions and 13 deletions
+44 -4
View File
@@ -731,6 +731,8 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
let get_field = own_field(vm, desc_val, get_si);
let set_field = own_field(vm, desc_val, set_si);
let writable_field = own_field(vm, desc_val, writable_si);
let enumerable_field = own_field(vm, desc_val, enumerable_si);
let configurable_field = own_field(vm, desc_val, configurable_si);
// 数组 length 是无 shape 槽的虚拟数据属性:需与普通已有属性一样参与描述符
// 缺省回填(writable 保持当前值、value 保持当前长度),其当前描述符由元素
@@ -779,10 +781,10 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
}
// 修改已有属性时缺省字段回填现有值,仅定义新属性时缺省才为 false。
let enumerable = own_field(vm, desc_val, enumerable_si)
let enumerable = enumerable_field
.map(oxide_runtime_api::to_boolean)
.unwrap_or_else(|| existing_meta.map(|m| m.attributes.enumerable()).unwrap_or(false));
let configurable = own_field(vm, desc_val, configurable_si)
let configurable = configurable_field
.map(oxide_runtime_api::to_boolean)
.unwrap_or_else(|| existing_meta.map(|m| m.attributes.configurable()).unwrap_or(false));
@@ -801,8 +803,9 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
})
};
let obj = unsafe { &mut *obj_ptr };
if has_accessor {
// 访问器描述符:解析 get/set(缺失字段回填现有属性)并做可调用性校验——
// 规范步序先于索引/约束检查。
let (get, set) = if has_accessor {
let get = get_field.unwrap_or_else(|| {
existing_meta
.filter(|m| m.is_accessor)
@@ -818,6 +821,43 @@ pub(crate) fn define_from_descriptor<H: VmHost>(
if (!get.is_undefined() && !is_callable(get)) || (!set.is_undefined() && !is_callable(set)) {
return Err("accessor descriptor get/set must be callable or undefined".to_string());
}
(get, set)
} else {
(JsValue::undefined(), JsValue::undefined())
};
// TA 数值索引臂:置于混入/可调用检查之后、define 路由之前。字段在场四检查
// 取原始描述符字段(非缺省回填后的属性位);有效索引值臂缺省读当前元素
// (通用描述符);数字无效索引零副作用直接失败;非规范数字串 / symbol 键
// 落真实自有属性路径。
if unsafe { &*obj_ptr }.is_typed_array_obj() {
match crate::typed_array::ta_index_gate(vm, unsafe { &*obj_ptr }, key_si) {
crate::typed_array::TaIndexGate::NumericValid(index) => {
if has_accessor
|| writable_field.is_some_and(|w| !oxide_runtime_api::to_boolean(w))
|| enumerable_field.is_some_and(|e| !oxide_runtime_api::to_boolean(e))
|| configurable_field.is_some_and(|c| !oxide_runtime_api::to_boolean(c))
{
return Err(
"cannot define property: TypedArray index only accepts a writable, enumerable, configurable data descriptor"
.to_string(),
);
}
let value = value_field.unwrap_or_else(|| {
crate::typed_array::typed_array_element_get(vm, unsafe { &*obj_ptr }, index)
.unwrap_or(JsValue::undefined())
});
return crate::typed_array::typed_array_element_define(vm, unsafe { &mut *obj_ptr }, index, value);
}
crate::typed_array::TaIndexGate::NumericInvalid => {
return Err("cannot define property: TypedArray index out of range".to_string());
}
crate::typed_array::TaIndexGate::Ordinary => {}
}
}
let obj = unsafe { &mut *obj_ptr };
if has_accessor {
vm.define_accessor_property(obj, key_si, get, set, PropAttributes::new(false, enumerable, configurable))?;
} else if has_data {
let value = if has_existing {
+21 -2
View File
@@ -543,11 +543,18 @@ pub fn typed_array_receiver_set<H: VmHost>(
/// 定义 TypedArray 整数索引元素(defineProperty 语义)。
///
/// # 步骤
/// 1. 取视图(内部状态非法返回防御背板 Err)。
/// 2. 界判:越界(含 detach:live 长 0)拒绝定义。
/// 3. 清 uncaught 槽后强转写入;强转失败把原值转存 define 专用槽。
///
/// # 边界与前提
/// - 索引越界:拒绝定义(Err),与整数索引 exotic 对象的 [[DefineOwnProperty]] 一致
///
/// # 副作用
/// - 把值 ToNumber 后写入底层 buffer
/// - 强转期用户回调抛错时原值入 define 专用槽,Object/Reflect 入口据此
/// 原值重抛
// ponytail: 不校验 writable 描述符——调用方把"省略 writable"折叠为 false,
// 无法与显式 false 区分;TA 元素天然可写,直接写入。
pub fn typed_array_element_define<H: VmHost>(
@@ -558,7 +565,14 @@ pub fn typed_array_element_define<H: VmHost>(
if index as usize >= ta_live_length(view) {
return Err("cannot define property: TypedArray index out of range".to_string());
}
write_typed_array_element(vm, view, index, value)
// 强转前清 uncaught 槽(length 路径同纪律):失败后槽内值必为本次强转
// 产生的原值。
vm.clear_uncaught_value();
let r = write_typed_array_element(vm, view, index, value);
if r.is_err() {
vm.move_uncaught_to_pending_length();
}
r
}
fn write_typed_array_element<H: VmHost>(
@@ -577,7 +591,12 @@ fn write_typed_array_element<H: VmHost>(
Ok(v) => v,
// 转换失败恢复为可捕获的 JS 异常:深度 0 原值入通道就地展开到外围
// catch(kind 保真),深度 >0 以 kind 前缀文本由原生调用边界恢复。
Err(err) => return vm.raise_captured(err),
Err(err) => {
// 原值放回 uncaught 槽:define 通道专用槽转存依赖槽内原值
// (set 通道经文本消费,槽不改变其可观测行为)。
vm.restore_uncaught_value(Some(err));
return vm.raise_captured(err);
}
};
// live 边界:越界(含 detach)静默不写(规范 IntegerIndexedElementSet 的
// IsValidIndexedAccess 臂),转换副作用已先发生。