fix: 迭代可选入口对非空不可调用 @@iterator 抛错并实现 IsHTMLDDA 宿主值

This commit is contained in:
2026-09-14 22:16:43 +00:00
parent 5cfd3162ba
commit 92df76565b
6 changed files with 143 additions and 16 deletions
+4 -1
View File
@@ -85,10 +85,13 @@ impl Vm {
JsType::Symbol => 5,
JsType::BigInt => 6,
JsType::Object => {
// 函数对象归为 "function"(语言类型是 object,typeof 有专属分支)。
// 函数对象归为 "function"(语言类型是 object,typeof 有专属分支);
// [[IsHTMLDDA]] 宿主对象按 "undefined" 报告(B.3.4,Type 仍是 object)。
let obj = unsafe { &*val.as_js_object_ptr() };
if obj.is_function() {
7
} else if obj.is_html_dda_obj() {
0
} else {
1
}
+7
View File
@@ -111,6 +111,13 @@ pub fn bind_test262_host(core: &Arc<KernelCore>, session: &KernelSession, global
);
let global_this = JsValue::from_js_object(global as *mut JsObject);
bind_global_value(core, &mut host, "global", global_this);
// [[IsHTMLDDA]] 宿主值(B.3.4):ToBoolean/typeof/宽松相等按 undefined 处理,
// Type 与其余强制转换按普通对象处理。无额外内部载荷,挂 Object.prototype。
let mut dda = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
dda.type_tag = JsObject::OBJ_TYPE_HTML_DDA;
let dda_ptr = Box::into_raw(Box::new(dda));
session.builtin_world().track_leaked_object(dda_ptr);
bind_global_value(core, &mut host, "IsHTMLDDA", JsValue::from_js_object(dda_ptr));
let host_ptr = Box::into_raw(Box::new(host));
session.builtin_world().track_leaked_object(host_ptr);
bind_global_value(core, global, "$262", JsValue::from_js_object(host_ptr));
@@ -2238,13 +2238,90 @@ fn typed_array_set_reads_array_like_source_by_index() {
let r = eval(
&mut vm,
"(() => {
const src = { 0: 7, 1: 8, length: 2 };
src[Symbol.iterator] = function* () { yield 99; };
const t = new Uint8Array(4);
t.set(src);
return t.join(',');
})()",
const src = { 0: 7, 1: 8, length: 2 };
src[Symbol.iterator] = function* () { yield 99; };
const t = new Uint8Array(4);
t.set(src);
return t.join(',');
})()",
)
.unwrap();
assert_eq!(to_str(&vm, r), "7,8,0,0");
}
#[test]
fn array_from_throws_when_iterator_not_callable() {
let mut vm = Vm::new();
// 红形钉:GetMethod-可选入口(Array.from)对"非空不可调用" @@iterator 须抛
// TypeError(GetMethod 步 4),红形为静默回退 array-like 得元素。自身置
// 对象/原始值两形皆红;null 仍走 array-like(对照不抛)。
let r = eval(
&mut vm,
"(() => {
const o = { length: 2, 0: 1, 1: 2 };
o[Symbol.iterator] = {};
let objErr;
try { objErr = Array.from(o).join(','); }
catch (e) { objErr = e.constructor.name; }
o[Symbol.iterator] = 5;
let primErr;
try { primErr = Array.from(o).join(','); }
catch (e) { primErr = e.constructor.name; }
o[Symbol.iterator] = null;
let nullFb;
try { nullFb = Array.from(o).join(','); }
catch (e) { nullFb = e.constructor.name; }
return objErr + '|' + primErr + '|' + nullFb;
})()",
)
.unwrap();
assert_eq!(to_str(&vm, r), "TypeError|TypeError|1,2");
}
#[test]
fn typed_array_ctor_throws_when_iterator_not_callable() {
let mut vm = Vm::new();
// 红形钉:TA 构造器(GetMethod-可选入口)对"非空不可调用" @@iterator 须抛
// TypeError,红形为静默回退 array-like(无 length 得 0 元素)。
let r = eval(
&mut vm,
"(() => {
const o = { length: 2, 0: 1, 1: 2 };
o[Symbol.iterator] = {};
let objErr;
try { objErr = new Uint8Array(o).length; }
catch (e) { objErr = e.constructor.name; }
o[Symbol.iterator] = true;
let boolErr;
try { boolErr = new Uint8Array(o).length; }
catch (e) { boolErr = e.constructor.name; }
return objErr + '|' + boolErr;
})()",
)
.unwrap();
assert_eq!(to_str(&vm, r), "TypeError|TypeError");
}
#[test]
fn typed_array_from_throws_when_iterator_not_callable() {
let mut vm = Vm::new();
// 红形钉:TypedArray.from(GetMethod-可选入口)对"非空不可调用" @@iterator
// 须抛 TypeError,红形为静默回退 array-like。
let r = eval(
&mut vm,
"(() => {
const o = { length: 2, 0: 1, 1: 2 };
o[Symbol.iterator] = {};
let objErr;
try { objErr = Uint8Array.from(o).length; }
catch (e) { objErr = e.constructor.name; }
o[Symbol.iterator] = 42;
let primErr;
try { primErr = Uint8Array.from(o).length; }
catch (e) { primErr = e.constructor.name; }
return objErr + '|' + primErr;
})()",
)
.unwrap();
assert_eq!(to_str(&vm, r), "TypeError|TypeError");
}