/* * Copyright (c) 2020, Matthew Olsson * Copyright (c) 2021, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include namespace JS { ProxyObject* ProxyObject::create(GlobalObject& global_object, Object& target, Object& handler) { return global_object.heap().allocate(global_object, target, handler, *global_object.object_prototype()); } ProxyObject::ProxyObject(Object& target, Object& handler, Object& prototype) : FunctionObject(prototype) , m_target(target) , m_handler(handler) { } ProxyObject::~ProxyObject() { } static Value property_name_to_value(VM& vm, PropertyName const& name) { VERIFY(name.is_valid()); if (name.is_symbol()) return name.as_symbol(); if (name.is_string()) return js_string(vm, name.as_string()); VERIFY(name.is_number()); return js_string(vm, String::number(name.as_number())); } // 10.5.1 [[GetPrototypeOf]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof ThrowCompletionOr ProxyObject::internal_get_prototype_of() const { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Let handler be O.[[ProxyHandler]]. // 2. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 3. Assert: Type(handler) is Object. // 4. Let target be O.[[ProxyTarget]]. // 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.getPrototypeOf)); // 6. If trap is undefined, then if (!trap) { // a. Return ? target.[[GetPrototypeOf]](). return TRY(m_target.internal_get_prototype_of()); } // 7. Let handlerProto be ? Call(trap, handler, « target »). auto handler_proto = TRY(vm.call(*trap, &m_handler, &m_target)); // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception. if (!handler_proto.is_object() && !handler_proto.is_null()) return vm.throw_completion(global_object, ErrorType::ProxyGetPrototypeOfReturn); // 9. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // 10. If extensibleTarget is true, return handlerProto. if (extensible_target) return handler_proto.is_null() ? nullptr : &handler_proto.as_object(); // 11. Let targetProto be ? target.[[GetPrototypeOf]](). auto* target_proto = TRY(m_target.internal_get_prototype_of()); // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception. if (!same_value(handler_proto, target_proto)) return vm.throw_completion(global_object, ErrorType::ProxyGetPrototypeOfNonExtensible); // 13. Return handlerProto. return handler_proto.is_null() ? nullptr : &handler_proto.as_object(); } // 10.5.2 [[SetPrototypeOf]] ( V ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v ThrowCompletionOr ProxyObject::internal_set_prototype_of(Object* prototype) { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Assert: Either Type(V) is Object or Type(V) is Null. // 2. Let handler be O.[[ProxyHandler]]. // 3. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 4. Assert: Type(handler) is Object. // 5. Let target be O.[[ProxyTarget]]. // 6. Let trap be ? GetMethod(handler, "setPrototypeOf"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.setPrototypeOf)); // 7. If trap is undefined, then if (!trap) { // a. Return ? target.[[SetPrototypeOf]](V). return m_target.internal_set_prototype_of(prototype); } // 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, V »)). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target, prototype)).to_boolean(); // 9. If booleanTrapResult is false, return false. if (!trap_result) return false; // 10. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // 11. If extensibleTarget is true, return true. if (extensible_target) return true; // 12. Let targetProto be ? target.[[GetPrototypeOf]](). auto* target_proto = TRY(m_target.internal_get_prototype_of()); // 13. If SameValue(V, targetProto) is false, throw a TypeError exception. if (!same_value(prototype, target_proto)) return vm.throw_completion(global_object, ErrorType::ProxySetPrototypeOfNonExtensible); // 14. Return true. return true; } // 10.5.3 [[IsExtensible]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible ThrowCompletionOr ProxyObject::internal_is_extensible() const { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Let handler be O.[[ProxyHandler]]. // 2. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 3. Assert: Type(handler) is Object. // 4. Let target be O.[[ProxyTarget]]. // 5. Let trap be ? GetMethod(handler, "isExtensible"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.isExtensible)); // 6. If trap is undefined, then if (!trap) { // a. Return ? IsExtensible(target). return m_target.is_extensible(); } // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target)).to_boolean(); // 8. Let targetResult be ? IsExtensible(target). auto target_result = TRY(m_target.is_extensible()); // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception. if (trap_result != target_result) return vm.throw_completion(global_object, ErrorType::ProxyIsExtensibleReturn); // 10. Return booleanTrapResult. return trap_result; } // 10.5.4 [[PreventExtensions]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions ThrowCompletionOr ProxyObject::internal_prevent_extensions() { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Let handler be O.[[ProxyHandler]]. // 2. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 3. Assert: Type(handler) is Object. // 4. Let target be O.[[ProxyTarget]]. // 5. Let trap be ? GetMethod(handler, "preventExtensions"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.preventExtensions)); // 6. If trap is undefined, then if (!trap) { // a. Return ? target.[[PreventExtensions]](). return m_target.internal_prevent_extensions(); } // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target)).to_boolean(); // 8. If booleanTrapResult is true, then if (trap_result) { // a. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // b. If extensibleTarget is true, throw a TypeError exception. if (extensible_target) return vm.throw_completion(global_object, ErrorType::ProxyPreventExtensionsReturn); } // 9. Return booleanTrapResult. return trap_result; } // 10.5.5 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p ThrowCompletionOr> ProxyObject::internal_get_own_property(const PropertyName& property_name) const { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Assert: IsPropertyKey(P) is true. VERIFY(property_name.is_valid()); // 2. Let handler be O.[[ProxyHandler]]. // 3. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 4. Assert: Type(handler) is Object. // 5. Let target be O.[[ProxyTarget]]. // 6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.getOwnPropertyDescriptor)); // 7. If trap is undefined, then if (!trap) { // a. Return ? target.[[GetOwnProperty]](P). return m_target.internal_get_own_property(property_name); } // 8. Let trapResultObj be ? Call(trap, handler, « target, P »). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target, property_name_to_value(vm, property_name))); // 9. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception. if (!trap_result.is_object() && !trap_result.is_undefined()) return vm.throw_completion(global_object, ErrorType::ProxyGetOwnDescriptorReturn); // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). auto target_descriptor = TRY(m_target.internal_get_own_property(property_name)); // 11. If trapResultObj is undefined, then if (trap_result.is_undefined()) { // a. If targetDesc is undefined, return undefined. if (!target_descriptor.has_value()) return Optional {}; // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception. if (!*target_descriptor->configurable) return vm.throw_completion(global_object, ErrorType::ProxyGetOwnDescriptorNonConfigurable); // c. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // d. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target) return vm.throw_completion(global_object, ErrorType::ProxyGetOwnDescriptorUndefinedReturn); // e. Return undefined. return Optional {}; } // 12. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // 13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj). auto result_desc = to_property_descriptor(global_object, trap_result); if (auto* exception = vm.exception()) return throw_completion(exception->value()); // 14. Call CompletePropertyDescriptor(resultDesc). result_desc.complete(); // 15. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc). auto valid = is_compatible_property_descriptor(extensible_target, result_desc, target_descriptor); // 16. If valid is false, throw a TypeError exception. if (!valid) return vm.throw_completion(global_object, ErrorType::ProxyGetOwnDescriptorInvalidDescriptor); // 17. If resultDesc.[[Configurable]] is false, then if (!*result_desc.configurable) { // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then if (!target_descriptor.has_value() || *target_descriptor->configurable) // i. Throw a TypeError exception. return vm.throw_completion(global_object, ErrorType::ProxyGetOwnDescriptorInvalidNonConfig); // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then if (result_desc.writable.has_value() && !*result_desc.writable) { // i. If targetDesc.[[Writable]] is true, throw a TypeError exception. if (*target_descriptor->writable) return vm.throw_completion(global_object, ErrorType::ProxyGetOwnDescriptorNonConfigurableNonWritable); } } // 18. Return resultDesc. return { result_desc }; } // 10.5.6 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc ThrowCompletionOr ProxyObject::internal_define_own_property(PropertyName const& property_name, PropertyDescriptor const& property_descriptor) { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Assert: IsPropertyKey(P) is true. VERIFY(property_name.is_valid()); // 2. Let handler be O.[[ProxyHandler]]. // 3. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 4. Assert: Type(handler) is Object. // 5. Let target be O.[[ProxyTarget]]. // 6. Let trap be ? GetMethod(handler, "defineProperty"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.defineProperty)); // 7. If trap is undefined, then if (!trap) { // a. Return ? target.[[DefineOwnProperty]](P, Desc). return m_target.internal_define_own_property(property_name, property_descriptor); } // 8. Let descObj be FromPropertyDescriptor(Desc). auto descriptor_object = from_property_descriptor(global_object, property_descriptor); // 9. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target, property_name_to_value(vm, property_name), descriptor_object)).to_boolean(); // 10. If booleanTrapResult is false, return false. if (!trap_result) return false; // 11. Let targetDesc be ? target.[[GetOwnProperty]](P). auto target_descriptor = TRY(m_target.internal_get_own_property(property_name)); // 12. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // 14. Else, let settingConfigFalse be false. bool setting_config_false = false; // 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then if (property_descriptor.configurable.has_value() && !*property_descriptor.configurable) { // a. Let settingConfigFalse be true. setting_config_false = true; } // 15. If targetDesc is undefined, then if (!target_descriptor.has_value()) { // a. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target) return vm.throw_completion(global_object, ErrorType::ProxyDefinePropNonExtensible); // b. If settingConfigFalse is true, throw a TypeError exception. if (setting_config_false) return vm.throw_completion(global_object, ErrorType::ProxyDefinePropNonConfigurableNonExisting); } // 16. Else, else { // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception. if (!is_compatible_property_descriptor(extensible_target, property_descriptor, target_descriptor)) return vm.throw_completion(global_object, ErrorType::ProxyDefinePropIncompatibleDescriptor); // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception. if (setting_config_false && *target_descriptor->configurable) return vm.throw_completion(global_object, ErrorType::ProxyDefinePropExistingConfigurable); // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then if (target_descriptor->is_data_descriptor() && !*target_descriptor->configurable && *target_descriptor->writable) { // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception. if (property_descriptor.writable.has_value() && !*property_descriptor.writable) return vm.throw_completion(global_object, ErrorType::ProxyDefinePropNonWritable); } } // 17. Return true. return true; } // 10.5.7 [[HasProperty]] ( P ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p ThrowCompletionOr ProxyObject::internal_has_property(PropertyName const& property_name) const { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Assert: IsPropertyKey(P) is true. VERIFY(property_name.is_valid()); // 2. Let handler be O.[[ProxyHandler]]. // 3. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 4. Assert: Type(handler) is Object. // 5. Let target be O.[[ProxyTarget]]. // 6. Let trap be ? GetMethod(handler, "has"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.has)); // 7. If trap is undefined, then if (!trap) { // a. Return ? target.[[HasProperty]](P). return m_target.internal_has_property(property_name); } // 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target, property_name_to_value(vm, property_name))).to_boolean(); // 9. If booleanTrapResult is false, then if (!trap_result) { // a. Let targetDesc be ? target.[[GetOwnProperty]](P). auto target_descriptor = TRY(m_target.internal_get_own_property(property_name)); // b. If targetDesc is not undefined, then if (target_descriptor.has_value()) { // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception. if (!*target_descriptor->configurable) return vm.throw_completion(global_object, ErrorType::ProxyHasExistingNonConfigurable); // ii. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // iii. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target) return vm.throw_completion(global_object, ErrorType::ProxyHasExistingNonExtensible); } } // 10. Return booleanTrapResult. return trap_result; } // 10.5.8 [[Get]] ( P, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver ThrowCompletionOr ProxyObject::internal_get(PropertyName const& property_name, Value receiver) const { VERIFY(!receiver.is_empty()); auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Assert: IsPropertyKey(P) is true. VERIFY(property_name.is_valid()); // 2. Let handler be O.[[ProxyHandler]]. // 3. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 4. Assert: Type(handler) is Object. // 5. Let target be O.[[ProxyTarget]]. // NOTE: We need to protect ourselves from a Proxy with the handler's prototype set to the // Proxy itself, which would by default bounce between these functions indefinitely and lead to // a stack overflow when the Proxy's (p) or Proxy handler's (h) Object::get() is called and the // handler doesn't have a `get` trap: // // 1. p -> ProxyObject::internal_get() <- you are here // 2. h -> Value::get_method() // 3. h -> Value::get() // 4. h -> Object::internal_get() // 5. h -> Object::internal_get_prototype_of() (result is p) // 6. goto 1 // // In JS code: `h = {}; p = new Proxy({}, h); h.__proto__ = p; p.foo // or h.foo` if (vm.did_reach_stack_space_limit()) return vm.throw_completion(global_object, ErrorType::CallStackSizeExceeded); // 6. Let trap be ? GetMethod(handler, "get"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.get)); // 7. If trap is undefined, then if (!trap) { // a. Return ? target.[[Get]](P, Receiver). return m_target.internal_get(property_name, receiver); } // 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target, property_name_to_value(vm, property_name), receiver)); // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). auto target_descriptor = TRY(m_target.internal_get_own_property(property_name)); // 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then if (target_descriptor.has_value() && !*target_descriptor->configurable) { // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then if (target_descriptor->is_data_descriptor() && !*target_descriptor->writable) { // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception. if (!same_value(trap_result, *target_descriptor->value)) return vm.throw_completion(global_object, ErrorType::ProxyGetImmutableDataProperty); } // b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then if (target_descriptor->is_accessor_descriptor() && !*target_descriptor->get) { // i. If trapResult is not undefined, throw a TypeError exception. if (!trap_result.is_undefined()) return vm.throw_completion(global_object, ErrorType::ProxyGetNonConfigurableAccessor); } } // 11. Return trapResult. return trap_result; } // 10.5.9 [[Set]] ( P, V, Receiver ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver ThrowCompletionOr ProxyObject::internal_set(PropertyName const& property_name, Value value, Value receiver) { VERIFY(!value.is_empty()); VERIFY(!receiver.is_empty()); auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Assert: IsPropertyKey(P) is true. VERIFY(property_name.is_valid()); // 2. Let handler be O.[[ProxyHandler]]. // 3. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 4. Assert: Type(handler) is Object. // 5. Let target be O.[[ProxyTarget]]. // 6. Let trap be ? GetMethod(handler, "set"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.set)); // 7. If trap is undefined, then if (!trap) { // a. Return ? target.[[Set]](P, V, Receiver). return m_target.internal_set(property_name, value, receiver); } // 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target, property_name_to_value(vm, property_name), value, receiver)).to_boolean(); // 9. If booleanTrapResult is false, return false. if (!trap_result) return false; // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). auto target_descriptor = TRY(m_target.internal_get_own_property(property_name)); // 11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then if (target_descriptor.has_value() && !*target_descriptor->configurable) { // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then if (target_descriptor->is_data_descriptor() && !*target_descriptor->writable) { // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception. if (!same_value(value, *target_descriptor->value)) return vm.throw_completion(global_object, ErrorType::ProxySetImmutableDataProperty); } // b. If IsAccessorDescriptor(targetDesc) is true, then if (target_descriptor->is_accessor_descriptor()) { // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception. if (!*target_descriptor->set) return vm.throw_completion(global_object, ErrorType::ProxySetNonConfigurableAccessor); } } // 12. Return true. return true; } // 10.5.10 [[Delete]] ( P ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p ThrowCompletionOr ProxyObject::internal_delete(PropertyName const& property_name) { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Assert: IsPropertyKey(P) is true. VERIFY(property_name.is_valid()); // 2. Let handler be O.[[ProxyHandler]]. // 3. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 4. Assert: Type(handler) is Object. // 5. Let target be O.[[ProxyTarget]]. // 6. Let trap be ? GetMethod(handler, "deleteProperty"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.deleteProperty)); // 7. If trap is undefined, then if (!trap) { // a. Return ? target.[[Delete]](P). return m_target.internal_delete(property_name); } // 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)). auto trap_result = TRY(vm.call(*trap, &m_handler, &m_target, property_name_to_value(vm, property_name))).to_boolean(); // 9. If booleanTrapResult is false, return false. if (!trap_result) return false; // 10. Let targetDesc be ? target.[[GetOwnProperty]](P). auto target_descriptor = TRY(m_target.internal_get_own_property(property_name)); // 11. If targetDesc is undefined, return true. if (!target_descriptor.has_value()) return true; // 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception. if (!*target_descriptor->configurable) return vm.throw_completion(global_object, ErrorType::ProxyDeleteNonConfigurable); // 13. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // 14. If extensibleTarget is false, throw a TypeError exception. if (!extensible_target) return vm.throw_completion(global_object, ErrorType::ProxyDeleteNonExtensible); // 15. Return true. return true; } // 10.5.11 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys ThrowCompletionOr ProxyObject::internal_own_property_keys() const { auto& vm = this->vm(); auto& global_object = this->global_object(); // 1. Let handler be O.[[ProxyHandler]]. // 2. If handler is null, throw a TypeError exception. if (m_is_revoked) return vm.throw_completion(global_object, ErrorType::ProxyRevoked); // 3. Assert: Type(handler) is Object. // 4. Let target be O.[[ProxyTarget]]. // 5. Let trap be ? GetMethod(handler, "ownKeys"). auto trap = TRY(Value(&m_handler).get_method(global_object, vm.names.ownKeys)); // 6. If trap is undefined, then if (!trap) { // a. Return ? target.[[OwnPropertyKeys]](). return m_target.internal_own_property_keys(); } // 7. Let trapResultArray be ? Call(trap, handler, « target »). auto trap_result_array = TRY(vm.call(*trap, &m_handler, &m_target)); // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »). HashTable unique_keys; auto trap_result = TRY(create_list_from_array_like(global_object, trap_result_array, [&](auto value) -> ThrowCompletionOr { auto& vm = global_object.vm(); if (!value.is_string() && !value.is_symbol()) return vm.throw_completion(global_object, ErrorType::ProxyOwnPropertyKeysNotStringOrSymbol); auto property_key = value.to_property_key(global_object); VERIFY(!vm.exception()); unique_keys.set(property_key, AK::HashSetExistingEntryBehavior::Keep); return {}; })); // 9. If trapResult contains any duplicate entries, throw a TypeError exception. if (unique_keys.size() != trap_result.size()) return vm.throw_completion(global_object, ErrorType::ProxyOwnPropertyKeysDuplicates); // 10. Let extensibleTarget be ? IsExtensible(target). auto extensible_target = TRY(m_target.is_extensible()); // 11. Let targetKeys be ? target.[[OwnPropertyKeys]](). auto target_keys = TRY(m_target.internal_own_property_keys()); // 12. Assert: targetKeys is a List whose elements are only String and Symbol values. // 13. Assert: targetKeys contains no duplicate entries. // 14. Let targetConfigurableKeys be a new empty List. auto target_configurable_keys = MarkedValueList { heap() }; // 15. Let targetNonconfigurableKeys be a new empty List. auto target_nonconfigurable_keys = MarkedValueList { heap() }; // 16. For each element key of targetKeys, do for (auto& key : target_keys) { // a. Let desc be ? target.[[GetOwnProperty]](key). auto descriptor = TRY(m_target.internal_get_own_property(PropertyName::from_value(global_object, key))); // b. If desc is not undefined and desc.[[Configurable]] is false, then if (descriptor.has_value() && !*descriptor->configurable) { // i. Append key as an element of targetNonconfigurableKeys. target_nonconfigurable_keys.append(key); } // c. Else, else { // i. Append key as an element of targetConfigurableKeys. target_configurable_keys.append(key); } } // 17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then if (extensible_target && target_nonconfigurable_keys.is_empty()) { // a. Return trapResult. return { move(trap_result) }; } // 18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult. auto unchecked_result_keys = MarkedValueList { heap() }; unchecked_result_keys.extend(trap_result); // 19. For each element key of targetNonconfigurableKeys, do for (auto& key : target_nonconfigurable_keys) { // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. if (!unchecked_result_keys.contains_slow(key)) return vm.throw_completion(global_object, ErrorType::FixmeAddAnErrorString); // b. Remove key from uncheckedResultKeys. unchecked_result_keys.remove_first_matching([&](auto& value) { return same_value(value, key); }); } // 20. If extensibleTarget is true, return trapResult. if (extensible_target) return { move(trap_result) }; // 21. For each element key of targetConfigurableKeys, do for (auto& key : target_configurable_keys) { // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception. if (!unchecked_result_keys.contains_slow(key)) return vm.throw_completion(global_object, ErrorType::FixmeAddAnErrorString); // b. Remove key from uncheckedResultKeys. unchecked_result_keys.remove_first_matching([&](auto& value) { return same_value(value, key); }); } // 22. If uncheckedResultKeys is not empty, throw a TypeError exception. if (!unchecked_result_keys.is_empty()) return vm.throw_completion(global_object, ErrorType::FixmeAddAnErrorString); // 23. Return trapResult. return { move(trap_result) }; } // 10.5.12 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist Value ProxyObject::call() { auto& vm = this->vm(); auto& global_object = this->global_object(); auto this_argument = vm.this_value(global_object); // A Proxy exotic object only has a [[Call]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Call]] internal method. if (!is_function()) { vm.throw_exception(global_object, ErrorType::NotAFunction, Value(this).to_string_without_side_effects()); return {}; } // 1. Let handler be O.[[ProxyHandler]]. // 2. If handler is null, throw a TypeError exception. if (m_is_revoked) { vm.throw_exception(global_object, ErrorType::ProxyRevoked); return {}; } // 3. Assert: Type(handler) is Object. // 4. Let target be O.[[ProxyTarget]]. // 5. Let trap be ? GetMethod(handler, "apply"). auto trap = TRY_OR_DISCARD(Value(&m_handler).get_method(global_object, vm.names.apply)); // 6. If trap is undefined, then if (!trap) { // a. Return ? Call(target, thisArgument, argumentsList). return static_cast(m_target).call(); } // 7. Let argArray be ! CreateArrayFromList(argumentsList). auto arguments_array = Array::create(global_object, 0); vm.for_each_argument([&](auto& argument) { arguments_array->indexed_properties().append(argument); }); // 8. Return ? Call(trap, handler, « target, thisArgument, argArray »). return TRY_OR_DISCARD(vm.call(*trap, &m_handler, &m_target, this_argument, arguments_array)); } // 10.5.13 [[Construct]] ( argumentsList, newTarget ), https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget Value ProxyObject::construct(FunctionObject& new_target) { auto& vm = this->vm(); auto& global_object = this->global_object(); // A Proxy exotic object only has a [[Construct]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Construct]] internal method. if (!is_function()) { vm.throw_exception(global_object, ErrorType::NotAConstructor, Value(this).to_string_without_side_effects()); return {}; } // 1. Let handler be O.[[ProxyHandler]]. // 2. If handler is null, throw a TypeError exception. if (m_is_revoked) { vm.throw_exception(global_object, ErrorType::ProxyRevoked); return {}; } // 3. Assert: Type(handler) is Object. // 4. Let target be O.[[ProxyTarget]]. // 5. Assert: IsConstructor(target) is true. // 6. Let trap be ? GetMethod(handler, "construct"). auto trap = TRY_OR_DISCARD(Value(&m_handler).get_method(global_object, vm.names.construct)); // 7. If trap is undefined, then if (!trap) { // a. Return ? Construct(target, argumentsList, newTarget). return static_cast(m_target).construct(new_target); } // 8. Let argArray be ! CreateArrayFromList(argumentsList). auto arguments_array = Array::create(global_object, 0); vm.for_each_argument([&](auto& argument) { arguments_array->indexed_properties().append(argument); }); // 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »). auto result = TRY_OR_DISCARD(vm.call(*trap, &m_handler, &m_target, arguments_array, &new_target)); // 10. If Type(newObj) is not Object, throw a TypeError exception. if (!result.is_object()) { vm.throw_exception(global_object, ErrorType::ProxyConstructBadReturnType); return {}; } // 11. Return newObj. return result; } void ProxyObject::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(&m_target); visitor.visit(&m_handler); } const FlyString& ProxyObject::name() const { VERIFY(is_function()); return static_cast(m_target).name(); } FunctionEnvironment* ProxyObject::new_function_environment(Object* new_target) { VERIFY(is_function()); return static_cast(m_target).new_function_environment(new_target); } }