mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
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));
|
ctor.set_data_meta(0u32, oxide_types::object::PropAttributes::new(false, false, false));
|
||||||
// .name (ctor[1]) = writable:false, enumerable:false, configurable:true
|
// .name (ctor[1]) = writable:false, enumerable:false, configurable:true
|
||||||
ctor.set_data_meta(1u32, oxide_types::object::PropAttributes::new(false, false, 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))
|
(P::new(proto), P::new(ctor))
|
||||||
}
|
}
|
||||||
@@ -1175,6 +1178,10 @@ impl BuiltinWorld {
|
|||||||
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
|
let new_shape = shape_forge.make_shape(proto.shape_id(), si);
|
||||||
proto.set_shape_id(new_shape);
|
proto.set_shape_id(new_shape);
|
||||||
proto.ensure_hash_props().push(wrapper_val);
|
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();
|
proto.bump_generation();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,6 +300,29 @@ impl Vm {
|
|||||||
bumpalo::collections::Vec::new_in(self.epoch.bump());
|
bumpalo::collections::Vec::new_in(self.epoch.bump());
|
||||||
let mut seen = std::collections::HashSet::new();
|
let mut seen = std::collections::HashSet::new();
|
||||||
let mut current = obj_val;
|
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;
|
let mut depth = 0usize;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
@@ -166,7 +166,8 @@ fn object_proto_has_own_property_call_works() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn object_proto_property_is_enumerable_call_works() {
|
fn object_proto_property_is_enumerable_call_works() {
|
||||||
let (_vm, result) = eval("Object.prototype.propertyIsEnumerable.call(Object, 'assign')").unwrap();
|
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]
|
#[test]
|
||||||
|
|||||||
@@ -159,12 +159,12 @@ fn eval_do_while_continue() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_for_in_enumerates_keys() {
|
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]
|
#[test]
|
||||||
fn eval_for_in_empty_object() {
|
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]
|
#[test]
|
||||||
@@ -175,7 +175,7 @@ fn eval_for_in_non_object_throws() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_for_in_nested() {
|
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]
|
#[test]
|
||||||
@@ -185,7 +185,7 @@ fn eval_for_in_break() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eval_for_in_continue() {
|
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]
|
#[test]
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ fn regression_throw_statement_preserves_syntax_error_kind() {
|
|||||||
fn regression_for_in_prototype_chain() {
|
fn regression_for_in_prototype_chain() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
eval("var c=0;for(var k in {a:1}){c=c+1;}c"),
|
eval("var c=0;for(var k in {a:1}){c=c+1;}c"),
|
||||||
"2",
|
"1",
|
||||||
"for-in should include inherited constructor from prototype"
|
"for-in must not include non-enumerable prototype properties like constructor"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user