From 9cfbbfd8d8a6b9ab6bdeb299196c7a61aa36e382 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Tue, 29 Mar 2022 00:18:15 +0300 Subject: [PATCH] LibJS: Implement the EnumerateObjectProperties AO --- Userland/Libraries/LibJS/Runtime/Object.cpp | 39 +++++++++++++++++++++ Userland/Libraries/LibJS/Runtime/Object.h | 4 +++ 2 files changed, 43 insertions(+) diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp index 3b461d5719..d83d9fc3b0 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.cpp +++ b/Userland/Libraries/LibJS/Runtime/Object.cpp @@ -1155,6 +1155,45 @@ ThrowCompletionOr Object::define_properties(Value properties) return this; } +// 14.7.5.9 EnumerateObjectProperties ( O ), https://tc39.es/ecma262/#sec-enumerate-object-properties +Optional Object::enumerate_object_properties(Function(Value)> callback) const +{ + // 1. Return an Iterator object (27.1.1.2) whose next method iterates over all the String-valued keys of enumerable properties of O. The iterator object is never directly accessible to ECMAScript code. The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below. + // * Returned property keys do not include keys that are Symbols. + // * Properties of the target object may be deleted during enumeration. + // * A property that is deleted before it is processed is ignored. + // * If new properties are added to the target object during enumeration, the newly added properties are not guaranteed to be processed in the active enumeration. + // * A property name will be returned at most once in any enumeration. + // * Enumerating the properties of the target object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively. + // * A property of a prototype is not processed if it has the same name as a property that has already been processed. + + HashTable visited; + + auto const* target = this; + while (target) { + auto own_keys = TRY(target->internal_own_property_keys()); + for (auto& key : own_keys) { + if (!key.is_string()) + continue; + FlyString property_key = key.as_string().string(); + if (visited.contains(property_key)) + continue; + auto descriptor = TRY(target->internal_get_own_property(property_key)); + if (!descriptor.has_value()) + continue; + visited.set(property_key); + if (!*descriptor->enumerable) + continue; + if (auto completion = callback(key); completion.has_value()) + return completion.release_value(); + } + + target = TRY(target->internal_get_prototype_of()); + }; + + return {}; +} + void Object::visit_edges(Cell::Visitor& visitor) { Cell::visit_edges(visitor); diff --git a/Userland/Libraries/LibJS/Runtime/Object.h b/Userland/Libraries/LibJS/Runtime/Object.h index 01012537c5..f7675dced5 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.h +++ b/Userland/Libraries/LibJS/Runtime/Object.h @@ -136,6 +136,10 @@ public: ThrowCompletionOr define_properties(Value properties); + // 14.7.5 The for-in, for-of, and for-await-of Statements + + Optional enumerate_object_properties(Function(Value)>) const; + // Implementation-specific storage abstractions Optional storage_get(PropertyKey const&) const;