diff --git a/crates/js/src/builtins_tests.rs b/crates/js/src/builtins_tests.rs index 3e8362e52..f95569749 100644 --- a/crates/js/src/builtins_tests.rs +++ b/crates/js/src/builtins_tests.rs @@ -100,7 +100,8 @@ fn object_functions_list_create_and_lock_objects() { ); truth("Object.getPrototypeOf(Object.create(null)) === null && Object.getPrototypeOf([]) === Array.prototype"); assert_eq!(string("Object.getOwnPropertyNames(Object('ab')).join()"), "0,1,length"); - assert_eq!(string("var a = [5]; a.k = 1; Object.getOwnPropertyNames(a).join()"), "length,0,k"); + assert_eq!(string("Object.getOwnPropertyNames([0, 1, 2]).join()"), "0,1,2,length"); + assert_eq!(string("var a = [5]; a.k = 1; Object.getOwnPropertyNames(a).join()"), "0,k,length"); // Object.keys lists names in the order for-in visits them (section 15.2.3.14). assert_eq!(string("var o = {b: 1, a: 2, 3: 3}, s = ''; for (var k in o) s += k; Object.keys(o).join('') === s ? s : 'differs'"), "ba3"); truth("var o = Object.seal({x: 1}); o.x = 2; delete o.x; o.y = 1; o.x === 2 && o.y === undefined && Object.isSealed(o) && !Object.isFrozen(o)"); diff --git a/crates/js/src/interp.rs b/crates/js/src/interp.rs index 096ad99e7..2293984ce 100644 --- a/crates/js/src/interp.rs +++ b/crates/js/src/interp.rs @@ -1173,27 +1173,31 @@ impl Interp { } /// The names of an object's own properties (section 15.2.3.4): a String object's indices and - /// an array's or String object's `length` first, then the rest in the order they were added, - /// which is the order `for-in` visits them. Each name takes a unit of fuel, and the heap for - /// the list is checked first. + /// `length` first, then the rest in the order they were added, which is the order `for-in` + /// visits them, then an array's `length`. Each name takes a unit of fuel, and the heap for the + /// list is checked first. pub(crate) fn own_keys(&mut self, id: ObjId) -> JsResult> { let o = self.object(id)?; let string_len = if let Class::String(s) = &o.class { s.len() } else { 0 }; - let has_length = matches!(o.class, Class::Array { .. } | Class::String(_)); - let count = o.props.len().saturating_add(string_len).saturating_add(usize::from(has_length)); + let is_string = matches!(o.class, Class::String(_)); + let is_array = matches!(o.class, Class::Array { .. }); + let count = o.props.len().saturating_add(string_len).saturating_add(usize::from(is_string || is_array)); self.reserve_heap(index_keys_bytes(string_len).saturating_add(count.saturating_mul(KEY_ENTRY_BYTES)))?; let mut out = Vec::with_capacity(count); for i in 0..string_len { self.use_fuel()?; out.push(index_key(u32::try_from(i).unwrap_or(u32::MAX))); } - if has_length { + if is_string { out.push(key("length")); } for _ in 0..self.object(id)?.props.len() { self.use_fuel()?; } out.extend(self.object(id)?.props.keys()); + if is_array { + out.push(key("length")); + } Ok(out) }