fix: 修复不符合 JS 语义约束的错误行为测试
This commit is contained in:
@@ -257,6 +257,9 @@ fn make_pair(
|
||||
ctor.set_data_meta(0u32, oxide_types::object::PropAttributes::new(false, false, false));
|
||||
// .name (ctor[1]) = writable:false, enumerable:false, configurable:true
|
||||
ctor.set_data_meta(1u32, oxide_types::object::PropAttributes::new(false, false, true));
|
||||
// .constructor (proto[0]) = writable:true, enumerable:false, configurable:true
|
||||
// (JS spec: prototype.constructor is non-enumerable so it doesn't leak into for-in)
|
||||
proto.set_data_meta(0u32, oxide_types::object::PropAttributes::new(true, false, true));
|
||||
|
||||
(P::new(proto), P::new(ctor))
|
||||
}
|
||||
@@ -1175,6 +1178,10 @@ impl BuiltinWorld {
|
||||
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
|
||||
proto.set_shape_id(new_shape);
|
||||
proto.ensure_hash_props().push(wrapper_val);
|
||||
// Built-in prototype methods are non-enumerable per the ES spec; otherwise
|
||||
// they leak into for-in enumeration (e.g. array push/pop showing up in `for k in []`).
|
||||
let method_pos = proto.hash_props_vec().map_or(0, |v| v.len() as u32).saturating_sub(1);
|
||||
proto.set_data_meta(method_pos, oxide_types::object::PropAttributes::new(true, false, true));
|
||||
proto.bump_generation();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -300,6 +300,29 @@ impl Vm {
|
||||
bumpalo::collections::Vec::new_in(self.epoch.bump());
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
let mut current = obj_val;
|
||||
|
||||
// Arrays store integer-indexed elements in prop_vec, which is not part of
|
||||
// the shape chain. Enumerate them (ES: array indices are enumerable string
|
||||
// keys, ordered ascending before other own keys).
|
||||
if current.is_object() {
|
||||
let arr = unsafe { &*current.as_js_object_ptr() };
|
||||
if arr.is_array() {
|
||||
for i in 0..arr.prop_vec_len() {
|
||||
let is_enum = arr
|
||||
.prop_meta_at(i)
|
||||
.map(|m| m.attributes.enumerable())
|
||||
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
|
||||
if is_enum {
|
||||
let idx = self.kernel_core.perm_interner().intern(&i.to_string()).0;
|
||||
keys_vec.push((
|
||||
JsValue::perm_string(self.kernel_core.perm_interner().string_ptr(idx)),
|
||||
idx,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut depth = 0usize;
|
||||
|
||||
loop {
|
||||
|
||||
@@ -166,7 +166,8 @@ fn object_proto_has_own_property_call_works() {
|
||||
#[test]
|
||||
fn object_proto_property_is_enumerable_call_works() {
|
||||
let (_vm, result) = eval("Object.prototype.propertyIsEnumerable.call(Object, 'assign')").unwrap();
|
||||
assert_eq!(result, JsValue::bool(true));
|
||||
// Built-in methods are non-enumerable per the ES spec.
|
||||
assert_eq!(result, JsValue::bool(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -159,12 +159,12 @@ fn eval_do_while_continue() {
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_enumerates_keys() {
|
||||
assert_eq!(eval("var n=0; for (k in {a:1,b:2}) { n=n+1; } n"), "3");
|
||||
assert_eq!(eval("var n=0; for (k in {a:1,b:2}) { n=n+1; } n"), "2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_empty_object() {
|
||||
assert_eq!(eval("var r=0; for (k in {}) { r=1; } r"), "1");
|
||||
assert_eq!(eval("var r=0; for (k in {}) { r=1; } r"), "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -175,7 +175,7 @@ fn eval_for_in_non_object_throws() {
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_nested() {
|
||||
assert_eq!(eval("var r=0; for (k in {a:{x:1}}) { for (j in {y:2}) { r=r+1; } } r"), "2");
|
||||
assert_eq!(eval("var r=0; for (k in {a:{x:1}}) { for (j in {y:2}) { r=r+1; } } r"), "1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -185,7 +185,7 @@ fn eval_for_in_break() {
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_continue() {
|
||||
assert_eq!(eval("var r=0; for (k in {a:1,b:2}) { if (k=='a') continue; r=r+1; } r"), "2");
|
||||
assert_eq!(eval("var r=0; for (k in {a:1,b:2}) { if (k=='a') continue; r=r+1; } r"), "1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -104,8 +104,8 @@ fn regression_throw_statement_preserves_syntax_error_kind() {
|
||||
fn regression_for_in_prototype_chain() {
|
||||
assert_eq!(
|
||||
eval("var c=0;for(var k in {a:1}){c=c+1;}c"),
|
||||
"2",
|
||||
"for-in should include inherited constructor from prototype"
|
||||
"1",
|
||||
"for-in must not include non-enumerable prototype properties like constructor"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user