From f0dd425492e320106b3a6c898a3360e4e0008f27 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Fri, 14 Apr 2023 17:05:02 +0200 Subject: [PATCH] LibJS: Add spec comments to Object --- Userland/Libraries/LibJS/Runtime/Object.cpp | 85 +++++++++++++++++---- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp index 816447c03c..d753894077 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.cpp +++ b/Userland/Libraries/LibJS/Runtime/Object.cpp @@ -429,23 +429,43 @@ ThrowCompletionOr> Object::enumerable_own_property_names(Pro // 7.3.26 CopyDataProperties ( target, source, excludedItems ), https://tc39.es/ecma262/#sec-copydataproperties ThrowCompletionOr Object::copy_data_properties(VM& vm, Value source, HashTable const& seen_names) { + // 1. If source is either undefined or null, return unused. if (source.is_nullish()) return {}; - auto from_object = MUST(source.to_object(vm)); + // 2. Let from be ! ToObject(source). + auto from = MUST(source.to_object(vm)); - for (auto& next_key_value : TRY(from_object->internal_own_property_keys())) { + // 3. Let keys be ? from.[[OwnPropertyKeys]](). + auto keys = TRY(from->internal_own_property_keys()); + + // 4. For each element nextKey of keys, do + for (auto& next_key_value : keys) { auto next_key = MUST(PropertyKey::from_value(vm, next_key_value)); + + // a. Let excluded be false. + // b. For each element e of excludedItems, do + // i. If SameValue(e, nextKey) is true, then + // 1. Set excluded to true. if (seen_names.contains(next_key)) continue; - auto desc = TRY(from_object->internal_get_own_property(next_key)); + // c. If excluded is false, then + // i. Let desc be ? from.[[GetOwnProperty]](nextKey). + auto desc = TRY(from->internal_get_own_property(next_key)); + + // ii. If desc is not undefined and desc.[[Enumerable]] is true, then if (desc.has_value() && desc->attributes().is_enumerable()) { - auto prop_value = TRY(from_object->get(next_key)); - TRY(create_data_property_or_throw(next_key, prop_value)); + // 1. Let propValue be ? Get(from, nextKey). + auto prop_value = TRY(from->get(next_key)); + + // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue). + MUST(create_data_property_or_throw(next_key, prop_value)); } } + + // 5. Return unused. return {}; } @@ -455,14 +475,18 @@ PrivateElement* Object::private_element_find(PrivateName const& name) if (!m_private_elements) return nullptr; - auto element = m_private_elements->find_if([&](auto const& element) { + // 1. If O.[[PrivateElements]] contains a PrivateElement pe such that pe.[[Key]] is P, then + auto it = m_private_elements->find_if([&](auto const& element) { return element.key == name; }); - if (element.is_end()) - return nullptr; + if (!it.is_end()) { + // a. Return pe. + return &(*it); + } - return &(*element); + // 2. Return empty. + return nullptr; } // 7.3.28 PrivateFieldAdd ( O, P, value ), https://tc39.es/ecma262/#sec-privatefieldadd @@ -518,58 +542,87 @@ ThrowCompletionOr Object::private_method_or_accessor_add(PrivateElement el return {}; } -// 7.3.30 PrivateGet ( O, P ), https://tc39.es/ecma262/#sec-privateget +// 7.3.31 PrivateGet ( O, P ), https://tc39.es/ecma262/#sec-privateget ThrowCompletionOr Object::private_get(PrivateName const& name) { auto& vm = this->vm(); + // 1. Let entry be PrivateElementFind(O, P). auto* entry = private_element_find(name); + + // 2. If entry is empty, throw a TypeError exception. if (!entry) return vm.throw_completion(ErrorType::PrivateFieldDoesNotExistOnObject, name.description); auto& value = entry->value; - if (entry->kind != PrivateElement::Kind::Accessor) + // 3. If entry.[[Kind]] is either field or method, then + if (entry->kind != PrivateElement::Kind::Accessor) { + // a. Return entry.[[Value]]. return value; + } + // Assert: entry.[[Kind]] is accessor. VERIFY(value.is_accessor()); + + // 6. Let getter be entry.[[Get]]. auto* getter = value.as_accessor().getter(); + + // 5. If entry.[[Get]] is undefined, throw a TypeError exception. if (!getter) return vm.throw_completion(ErrorType::PrivateFieldGetAccessorWithoutGetter, name.description); - // 8. Return ? Call(getter, Receiver). + // 7. Return ? Call(getter, O). return TRY(call(vm, *getter, this)); } -// 7.3.31 PrivateSet ( O, P, value ), https://tc39.es/ecma262/#sec-privateset +// 7.3.32 PrivateSet ( O, P, value ), https://tc39.es/ecma262/#sec-privateset ThrowCompletionOr Object::private_set(PrivateName const& name, Value value) { auto& vm = this->vm(); + // 1. Let entry be PrivateElementFind(O, P). auto* entry = private_element_find(name); + + // 2. If entry is empty, throw a TypeError exception. if (!entry) return vm.throw_completion(ErrorType::PrivateFieldDoesNotExistOnObject, name.description); + // 3. If entry.[[Kind]] is field, then if (entry->kind == PrivateElement::Kind::Field) { + // a. Set entry.[[Value]] to value. entry->value = value; return {}; - } else if (entry->kind == PrivateElement::Kind::Method) { + } + // 4. Else if entry.[[Kind]] is method, then + else if (entry->kind == PrivateElement::Kind::Method) { + // a. Throw a TypeError exception. return vm.throw_completion(ErrorType::PrivateFieldSetMethod, name.description); } + // 5. Else, + + // a. Assert: entry.[[Kind]] is accessor. VERIFY(entry->kind == PrivateElement::Kind::Accessor); auto& accessor = entry->value; VERIFY(accessor.is_accessor()); + + // c. Let setter be entry.[[Set]]. auto* setter = accessor.as_accessor().setter(); + + // b. If entry.[[Set]] is undefined, throw a TypeError exception. if (!setter) return vm.throw_completion(ErrorType::PrivateFieldSetAccessorWithoutSetter, name.description); + // d. Perform ? Call(setter, O, « value »). TRY(call(vm, *setter, this, value)); + + // 6. Return unused. return {}; } -// 7.3.32 DefineField ( receiver, fieldRecord ), https://tc39.es/ecma262/#sec-definefield +// 7.3.33 DefineField ( receiver, fieldRecord ), https://tc39.es/ecma262/#sec-definefield ThrowCompletionOr Object::define_field(ClassFieldDefinition const& field) { auto& vm = this->vm(); @@ -605,7 +658,7 @@ ThrowCompletionOr Object::define_field(ClassFieldDefinition const& field) return {}; } -// 7.3.33 InitializeInstanceElements ( O, constructor ), https://tc39.es/ecma262/#sec-initializeinstanceelements +// 7.3.34 InitializeInstanceElements ( O, constructor ), https://tc39.es/ecma262/#sec-initializeinstanceelements ThrowCompletionOr Object::initialize_instance_elements(ECMAScriptFunctionObject& constructor) { // 1. Let methods be the value of constructor.[[PrivateMethods]].