feat: TypedArray Get/Has 接统一数值键门三态分派

This commit is contained in:
2026-09-23 06:30:37 +00:00
parent 9653598428
commit 897cdf678e
3 changed files with 119 additions and 15 deletions
+16 -11
View File
@@ -214,11 +214,16 @@ impl Vm {
/// 存在性判定(规范 HasProperty):自身与原型链逐级检查,任一层 P 为自有
/// 属性即存在。数组 length 虚拟属性恒存在;数组元素区 hole 视同缺失;
/// TypedArray 整数索引按视图长度判在界;其余按 shape 槽判定。
/// 其余按 shape 槽判定。
///
/// 与 `resolve_property` 的差异:原型链上每层都检查数组元素区与 TypedArray
/// 元素(`resolve_property` 只在顶层检查元素区,链上仅走 shape 槽),
/// 继承自父数组/TypedArray 的索引属性在此判存在。
/// 顶层 TypedArray 经统一数值键门(exotic [[HasProperty]]):界内整数索引
/// 判存在;数字无效键(负/分数/±Infinity/NaN/越界,含 "-0" 特例)立即
/// false,不查自身命名属性也不走原型链;非规范数字串落普通路径。原型链上
/// 的 TA 按普通对象查 shape 槽。
///
/// 与 `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);
@@ -227,13 +232,13 @@ impl Vm {
if obj.is_array() && prop_name_si == length_si {
return true;
}
if obj.is_typed_array_obj() {
if let Some((index, len)) =
oxide_builtins::typed_array::typed_array_integer_index(self, obj, prop_name_si)
{
if index < len {
return true;
}
// 统一数值键门(exotic [[HasProperty]]):同 `ordinary_get_inner`
// 的口径,门只落在顶层对象。
if obj.is_typed_array_obj() && depth == 0 {
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() {
+14 -3
View File
@@ -12,7 +12,8 @@ impl Vm {
/// # 步骤
/// 1. 数组 length 键返回逻辑长度值,不落属性存储。
/// 2. 数组整数索引在元素区命中且非 hole 时返回元素值;访问器元素触发 getter。
/// 3. TypedArray 整数索引读底层 buffer。
/// 3. 顶层 TypedArray 经统一数值键门:界内整数读底层 buffer,数字无效键
/// 立即 undefined,非规范数字串落普通路径。
/// 4. 命名属性槽命中返回槽值;accessor 触发 getter。
/// 5. 全部 miss 时沿原型链逐层查找,深度以 `MAX_PROTO_CHAIN_DEPTH` 为界。
///
@@ -99,10 +100,20 @@ impl Vm {
}
}
}
if obj.is_typed_array_obj() {
if let Some(index) = self.array_index_from_property_key(prop_name_si) {
// 统一数值键门(exotic [[Get]]):界内整数读底层 buffer;数字无效
// 键(负/分数/±Infinity/NaN/越界,含 "-0" 特例)立即 undefined 不
// 查自身命名属性也不走原型链;非规范数字串落下方普通属性路径。
// 门只落在顶层对象:原型链上的 TA 维持普通 shape 槽查找。
if obj.is_typed_array_obj() && depth == 0 {
match oxide_builtins::typed_array::ta_index_gate(self, obj, prop_name_si) {
oxide_builtins::typed_array::TaIndexGate::NumericValid(index) => {
return oxide_builtins::typed_array::typed_array_element_get(self, obj, index);
}
oxide_builtins::typed_array::TaIndexGate::NumericInvalid => {
return Ok(JsValue::undefined());
}
oxide_builtins::typed_array::TaIndexGate::Ordinary => {}
}
}
if let Some(pos) = self.get_own_property_slot(obj, prop_name_si) {
if let Some(meta) = obj.prop_meta_at(pos) {
@@ -750,3 +750,91 @@ fn typed_array_plain_member_call_in_class_ctor_not_construct() {
.unwrap();
assert!(result.as_bool());
}
#[test]
fn ta_gate_get_numeric_invalid_undefined() {
// 数字无效键(分数/负/"-0"/越界整数)读 undefined:exotic 数字臂不走
// 原型链,原型同键抛 getter 不被触达。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { var ta = new Int8Array([42, 43]); \
var keys = ['1.1', '-1', '-0', '2']; \
for (var i = 0; i < keys.length; i++) { \
Object.defineProperty(Int8Array.prototype, keys[i], { \
get: function () { throw new Error('OrdinaryGet was called'); } }); } \
for (var i = 0; i < keys.length; i++) { \
if (ta[keys[i]] !== undefined) return false; } \
return true; })()",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn ta_gate_has_numeric_invalid_false() {
// 数字无效键 in 判 false:exotic 数字臂不走原型链,原型同键数据属性
// 不被触达(防假阳)。length-1 样本覆盖越界整数键。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { var ta = new Int8Array([42]); \
var keys = ['1.1', '0.000001', '-1', '-0']; \
for (var i = 0; i < keys.length; i++) { \
Int8Array.prototype[keys[i]] = 'test262'; } \
Int8Array.prototype[1] = 'test262'; \
for (var i = 0; i < keys.length; i++) { \
if (keys[i] in ta) return false; } \
return !(1 in ta); })()",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn ta_gate_ordinary_arm_proto_walk() {
// 非规范数字串(round-trip 不成)落普通属性路径:缺失读 undefined,
// 原型预置后继承读值且 in 判 true;"+1" 与 "1" 独立成键互不干扰。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { var ta = new Int8Array([42]); \
var keys = ['1.0', '+1', '1000000000000000000000', '0.0000001']; \
for (var i = 0; i < keys.length; i++) { \
if (ta[keys[i]] !== undefined) return false; } \
for (var i = 0; i < keys.length; i++) { \
Int8Array.prototype[keys[i]] = 'test262'; \
if (ta[keys[i]] !== 'test262') return false; \
if (!(keys[i] in ta)) return false; } \
return ta['1'] === undefined; })()",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn ta_gate_valid_arm_no_drift() {
// 界内整数臂回归钉:读元素值、in 判 true、length 不变(Valid 臂零漂移)。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"var ta = new Int8Array([42, 7]); \
ta[0] === 42 && (0 in ta) && (1 in ta) && ta.length === 2",
)
.unwrap();
assert!(result.as_bool());
}
#[test]
fn ta_gate_detached_numeric_invalid() {
// detach 后 live 长 0:界内整数键归数字无效,读 undefined、in 判 false。
let mut vm = Vm::new();
let result = eval(
&mut vm,
"(function () { var ab = new ArrayBuffer(8); var ta = new Int8Array(ab); \
ta[0] = 9; ab.transfer(); \
return ta[0] === undefined && !(0 in ta); })()",
)
.unwrap();
assert!(result.as_bool());
}