ソースを参照

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.
Daniel Bertalan 2 年 前
コミット
d0dce5c60f
1 ファイル変更18 行追加3 行削除
  1. 18 3
      Userland/Libraries/LibJS/Bytecode/Op.cpp

+ 18 - 3
Userland/Libraries/LibJS/Bytecode/Op.cpp

@@ -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 {