diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp index 1d31293670..cb5b26e6fa 100644 --- a/Userland/Libraries/LibJS/Runtime/Object.cpp +++ b/Userland/Libraries/LibJS/Runtime/Object.cpp @@ -462,23 +462,49 @@ PrivateElement* Object::private_element_find(PrivateName const& name) // 7.3.28 PrivateFieldAdd ( O, P, value ), https://tc39.es/ecma262/#sec-privatefieldadd ThrowCompletionOr Object::private_field_add(PrivateName const& name, Value value) { + // 1. If the host is a web browser, then + // a. Perform ? HostEnsureCanAddPrivateElement(O). + // NOTE: Since LibJS has no way of knowing whether it is in a browser we just always call the hook. + TRY(vm().host_ensure_can_add_private_element(*this)); + + // 2. Let entry be PrivateElementFind(O, P). + // 3. If entry is not empty, throw a TypeError exception. if (auto* entry = private_element_find(name); entry) return vm().throw_completion(global_object(), ErrorType::PrivateFieldAlreadyDeclared, name.description); + if (!m_private_elements) m_private_elements = make>(); + + // 4. Append PrivateElement { [[Key]]: P, [[Kind]]: field, [[Value]]: value } to O.[[PrivateElements]]. m_private_elements->empend(name, PrivateElement::Kind::Field, value); + + // 5. Return unused. return {}; } // 7.3.29 PrivateMethodOrAccessorAdd ( O, method ), https://tc39.es/ecma262/#sec-privatemethodoraccessoradd ThrowCompletionOr Object::private_method_or_accessor_add(PrivateElement element) { + // 1. Assert: method.[[Kind]] is either method or accessor. VERIFY(element.kind == PrivateElement::Kind::Method || element.kind == PrivateElement::Kind::Accessor); + + // 2. If the host is a web browser, then + // a. Perform ? HostEnsureCanAddPrivateElement(O). + // NOTE: Since LibJS has no way of knowing whether it is in a browser we just always call the hook. + TRY(vm().host_ensure_can_add_private_element(*this)); + + // 3. Let entry be PrivateElementFind(O, method.[[Key]]). + // 4. If entry is not empty, throw a TypeError exception. if (auto* entry = private_element_find(element.key); entry) return vm().throw_completion(global_object(), ErrorType::PrivateFieldAlreadyDeclared, element.key.description); + if (!m_private_elements) m_private_elements = make>(); + + // 5. Append method to O.[[PrivateElements]]. m_private_elements->append(move(element)); + + // 6. Return unused. return {}; } diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index a7a12df189..5156515ade 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -124,6 +124,21 @@ VM::VM(OwnPtr custom_data) return {}; }; + host_ensure_can_add_private_element = [](Object&) -> ThrowCompletionOr { + // The host-defined abstract operation HostEnsureCanAddPrivateElement takes argument O (an Object) + // and returns either a normal completion containing unused or a throw completion. + // It allows host environments to prevent the addition of private elements to particular host-defined exotic objects. + // An implementation of HostEnsureCanAddPrivateElement must conform to the following requirements: + // - If O is not a host-defined exotic object, this abstract operation must return NormalCompletion(unused) and perform no other steps. + // - Any two calls of this abstract operation with the same argument must return the same kind of Completion Record. + // The default implementation of HostEnsureCanAddPrivateElement is to return NormalCompletion(unused). + return {}; + + // This abstract operation is only invoked by ECMAScript hosts that are web browsers. + // NOTE: Since LibJS has no way of knowing whether the current environment is a browser we always + // call HostEnsureCanAddPrivateElement when needed. + }; + #define __JS_ENUMERATE(SymbolName, snake_name) \ m_well_known_symbol_##snake_name = js_symbol(*this, "Symbol." #SymbolName, false); JS_ENUMERATE_WELL_KNOWN_SYMBOLS diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 4f7dc2b8db..9a5b4c71c3 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -229,6 +229,7 @@ public: Function()>, Realm*)> host_enqueue_promise_job; Function host_make_job_callback; Function(Realm&)> host_ensure_can_compile_strings; + Function(Object&)> host_ensure_can_add_private_element; private: explicit VM(OwnPtr);