LibJS/Bytecode: Handle shadowed non-enumerable properties in for-in

Invariants 5 and 6 of the `EnumerateObjectProperties` AO mean that we
must not include an enumerate property if there is a non-enumerable
property higher up the prototype chain with the same name. The previous
implementation did not adhere to this, as `EnumerableOwnPropertyNames`
does not carry information about present but non-enumerable properties.
This commit is contained in:
Daniel Bertalan 2023-07-06 09:12:29 +02:00 committed by Andreas Kling
parent f21a30e45f
commit d0dce5c60f
Notes: sideshowbarker 2024-07-18 01:43:16 +09:00

View file

@ -1156,18 +1156,33 @@ ThrowCompletionOr<void> GetObjectPropertyIterator::execute_impl(Bytecode::Interp
// 9- Property attributes of the target object must be obtained by calling its [[GetOwnProperty]] internal method
// Invariant 3 effectively allows the implementation to ignore newly added keys, and we do so (similar to other implementations).
// Invariants 1 and 6 through 9 are implemented in `enumerable_own_property_names`, which implements the EnumerableOwnPropertyNames AO.
auto& vm = interpreter.vm();
auto object = TRY(interpreter.accumulator().to_object(vm));
// Note: While the spec doesn't explicitly require these to be ordered, it says that the values should be retrieved via OwnPropertyKeys,
// so we just keep the order consistent anyway.
OrderedHashTable<PropertyKey> properties;
OrderedHashTable<PropertyKey> non_enumerable_properties;
HashTable<NonnullGCPtr<Object>> seen_objects;
// Collect all keys immediately (invariant no. 5)
for (auto object_to_check = GCPtr { object.ptr() }; object_to_check && !seen_objects.contains(*object_to_check); object_to_check = TRY(object_to_check->internal_get_prototype_of())) {
seen_objects.set(*object_to_check);
for (auto& key : TRY(object_to_check->enumerable_own_property_names(Object::PropertyKind::Key))) {
properties.set(TRY(PropertyKey::from_value(vm, key)));
for (auto& key : TRY(object_to_check->internal_own_property_keys())) {
if (key.is_symbol())
continue;
auto property_key = TRY(PropertyKey::from_value(vm, key));
// If there is a non-enumerable property higher up the prototype chain with the same key,
// we mustn't include this property even if it's enumerable (invariant no. 5 and 6)
if (non_enumerable_properties.contains(property_key))
continue;
if (properties.contains(property_key))
continue;
auto descriptor = TRY(object_to_check->internal_get_own_property(property_key));
if (!*descriptor->enumerable)
non_enumerable_properties.set(move(property_key));
else
properties.set(move(property_key));
}
}
IteratorRecord iterator {