mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
fix: for-in 右值原始值装箱后枚举
This commit is contained in:
@@ -461,7 +461,7 @@ impl Vm {
|
||||
|
||||
pub(crate) fn dispatch_for_in_init(&mut self, a: usize) -> Result<(), String> {
|
||||
vm_trace!("FOR_IN_INIT r{}={:?}", a, self.regs[a]);
|
||||
let obj_val = self.regs[a];
|
||||
let mut obj_val = self.regs[a];
|
||||
if obj_val.is_null() || obj_val.is_undefined() {
|
||||
// null/undefined 枚举不到任何键——是空 for-in,而非 TypeError。
|
||||
let keys_vec: bumpalo::collections::Vec<(JsValue, u32)> =
|
||||
@@ -471,8 +471,8 @@ impl Vm {
|
||||
return Ok(());
|
||||
}
|
||||
if !obj_val.is_object() {
|
||||
// 未支持:其它基本类型的 ToObject 强转尚未实现,在此之前抛 TypeError 是正确行为。
|
||||
return self.raise_type_error("for-in right-hand side is not an object");
|
||||
// ToObject:字符串盒构造期已物化索引属性面,其余原始值盒无自身可枚举属性。
|
||||
obj_val = oxide_runtime_api::to_object(obj_val, self)?;
|
||||
}
|
||||
|
||||
let mut keys_vec: Vec<(JsValue, u32)> = Vec::new();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
//! 字符串盒(String exotic)构造期物化钉测:`boxed_value` 专属载荷与字符索引/
|
||||
//! length 固有属性面。覆盖 test262 盒面红转绿的语义面——索引/length 读、in/
|
||||
//! 枚举、描述符、seal/freeze、defineProperty 同值改写放行/异值与属性面变更
|
||||
//! 拒绝、写保护(strict/sloppy)、delete 返 false、Object.assign/解构消费面、
|
||||
//! %StringPrototype% 自身盒面、迭代器/Array.from 载荷读、@@toStringTag 覆盖。
|
||||
//! 枚举、for-in 原始值右值装箱、描述符、seal/freeze、defineProperty 同值改写
|
||||
//! 放行/异值与属性面变更拒绝、写保护(strict/sloppy)、delete 返 false、
|
||||
//! Object.assign/解构消费面、%StringPrototype% 自身盒面、迭代器/Array.from 载荷读、
|
||||
//! @@toStringTag 覆盖。
|
||||
//!
|
||||
//! 语义对齐规范 String exotic:索引属性 writable:false / enumerable:true /
|
||||
//! configurable:false,length writable:false / enumerable:false /
|
||||
@@ -67,6 +68,28 @@ fn forin_enumerates_indexes_only() {
|
||||
assert!(r.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forin_primitive_string() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"(function(){ const keys = []; for (const k in 'ab') keys.push(k); return keys.length === 2 && keys[0] === '0' && keys[1] === '1'; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(r.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forin_primitive_no_keys() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"(function(){ const a = []; for (const k in 5) a.push(k); const b = []; for (const k in true) b.push(k); const c = []; for (const k in '') c.push(k); return a.length === 0 && b.length === 0 && c.length === 0; })()",
|
||||
)
|
||||
.unwrap();
|
||||
assert!(r.as_bool());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keys_and_own_keys() {
|
||||
let mut vm = Vm::new();
|
||||
|
||||
@@ -177,9 +177,9 @@ fn eval_for_in_empty_object() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_non_object_throws() {
|
||||
let result = eval("for (k in 42) {}");
|
||||
assert!(result.contains("TypeError"), "expected TypeError, got: {result}");
|
||||
fn eval_for_in_primitive_number() {
|
||||
// 右值经 ToObject 装箱后枚举:数字盒无自身可枚举属性,空枚举。
|
||||
assert_eq!(eval("var n=0; for (k in 42) { n=n+1; } n"), "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -51,9 +51,11 @@ fn for_in_null_does_not_throw() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn for_in_number_primitive_currently_throws() {
|
||||
// 过渡行为:非 null/undefined 基本类型的 ToObject 强转尚未实现,
|
||||
// 因此数字右侧仍抛 TypeError。ToObject 落地后它应变成空循环,届时本断言需变更。
|
||||
let out = eval("for(var k in 42){}");
|
||||
assert!(out.contains("TypeError"), "expected TypeError for number primitive, got: {out}");
|
||||
fn for_in_number_primitive_empty_loop() {
|
||||
// 右值经 ToObject 装箱:数字盒无自身可枚举属性,空循环。
|
||||
assert_eq!(
|
||||
eval("var r=0;for(var k in 42){r=r+1;}r"),
|
||||
"0",
|
||||
"for-in number primitive iterates zero times"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user