LibJS: Add and use ValidateNonRevokedProxy AO

This refactor is from two editorial changes to the spec from a while
back.

44d1cae2b2
21ffeee869
This commit is contained in:
Andrew Kaster 2025-04-23 01:30:43 -06:00 committed by Jelle Raaijmakers
commit 9bae24cc4a
Notes: github-actions[bot] 2025-04-24 08:38:42 +00:00
4 changed files with 107 additions and 122 deletions

View file

@ -175,29 +175,26 @@ ThrowCompletionOr<Realm*> get_function_realm(VM& vm, FunctionObject const& funct
} }
// 2. If obj is a bound function exotic object, then // 2. If obj is a bound function exotic object, then
if (is<BoundFunction>(function)) { if (auto const* bound_function = as_if<BoundFunction>(function)) {
auto& bound_function = static_cast<BoundFunction const&>(function); // a. Let boundTargetFunction be obj.[[BoundTargetFunction]].
auto& bound_target_function = bound_function->bound_target_function();
// a. Let target be obj.[[BoundTargetFunction]]. // b. Return ? GetFunctionRealm(boundTargetFunction).
auto& target = bound_function.bound_target_function(); return get_function_realm(vm, bound_target_function);
// b. Return ? GetFunctionRealm(target).
return get_function_realm(vm, target);
} }
// 3. If obj is a Proxy exotic object, then // 3. If obj is a Proxy exotic object, then
if (is<ProxyObject>(function)) { if (auto const* proxy = as_if<ProxyObject>(function)) {
auto& proxy = static_cast<ProxyObject const&>(function); // a. a. Perform ? ValidateNonRevokedProxy(obj).
TRY(proxy->validate_non_revoked_proxy());
// a. If obj.[[ProxyHandler]] is null, throw a TypeError exception.
if (proxy.is_revoked())
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// b. Let proxyTarget be obj.[[ProxyTarget]]. // b. Let proxyTarget be obj.[[ProxyTarget]].
auto& proxy_target = proxy.target(); auto& proxy_target = proxy->target();
// c. Return ? GetFunctionRealm(proxyTarget). // c. Assert: proxyTarget is a function object.
VERIFY(proxy_target.is_function()); VERIFY(proxy_target.is_function());
// d. Return ? GetFunctionRealm(proxyTarget).
return get_function_realm(vm, static_cast<FunctionObject const&>(proxy_target)); return get_function_realm(vm, static_cast<FunctionObject const&>(proxy_target));
} }

View file

@ -67,14 +67,12 @@ ThrowCompletionOr<Object*> ProxyObject::internal_get_prototype_of() const
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "getPrototypeOf"). // 5. Let trap be ? GetMethod(handler, "getPrototypeOf").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.getPrototypeOf)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.getPrototypeOf));
@ -117,14 +115,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_set_prototype_of(Object* prototype
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "setPrototypeOf"). // 5. Let trap be ? GetMethod(handler, "setPrototypeOf").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.setPrototypeOf)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.setPrototypeOf));
@ -167,14 +163,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_is_extensible() const
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "isExtensible"). // 5. Let trap be ? GetMethod(handler, "isExtensible").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.isExtensible)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.isExtensible));
@ -206,14 +200,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_prevent_extensions()
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "preventExtensions"). // 5. Let trap be ? GetMethod(handler, "preventExtensions").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.preventExtensions)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.preventExtensions));
@ -248,14 +240,12 @@ ThrowCompletionOr<Optional<PropertyDescriptor>> ProxyObject::internal_get_own_pr
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor"). // 5. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.getOwnPropertyDescriptor)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.getOwnPropertyDescriptor));
@ -339,14 +329,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_define_own_property(PropertyKey co
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "defineProperty"). // 5. Let trap be ? GetMethod(handler, "defineProperty").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.defineProperty)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.defineProperty));
@ -421,14 +409,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_has_property(PropertyKey const& pr
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// NOTE: We need to protect ourselves from a Proxy with the handler's prototype set to the // 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 // Proxy itself, which would by default bounce between these functions indefinitely and lead to
@ -490,14 +476,12 @@ ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_k
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// NOTE: We need to protect ourselves from a Proxy with its (or handler's) prototype set to the // NOTE: We need to protect ourselves from a Proxy with its (or handler's) prototype set to the
// Proxy itself, which would by default bounce between these functions indefinitely and lead to // Proxy itself, which would by default bounce between these functions indefinitely and lead to
@ -560,14 +544,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_ke
VERIFY(!value.is_special_empty_value()); VERIFY(!value.is_special_empty_value());
VERIFY(!receiver.is_special_empty_value()); VERIFY(!receiver.is_special_empty_value());
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// NOTE: We need to protect ourselves from a Proxy with its prototype set to the // NOTE: We need to protect ourselves from a Proxy with its prototype set to the
// Proxy itself, which would by default bounce between these functions indefinitely and lead to // Proxy itself, which would by default bounce between these functions indefinitely and lead to
@ -631,14 +613,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_delete(PropertyKey const& property
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "deleteProperty"). // 5. Let trap be ? GetMethod(handler, "deleteProperty").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.deleteProperty)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.deleteProperty));
@ -685,14 +665,12 @@ ThrowCompletionOr<GC::RootVector<Value>> ProxyObject::internal_own_property_keys
auto& vm = this->vm(); auto& vm = this->vm();
// 1. Let handler be O.[[ProxyHandler]]. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. If handler is null, throw a TypeError exception. // 2. Let target be O.[[ProxyTarget]].
if (m_is_revoked) // 3. Let handler be O.[[ProxyHandler]].
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked); // 4. Assert: handler is an Object.
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "ownKeys"). // 5. Let trap be ? GetMethod(handler, "ownKeys").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.ownKeys)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.ownKeys));
@ -808,18 +786,16 @@ ThrowCompletionOr<Value> ProxyObject::internal_call(Value this_argument, Readonl
auto& vm = this->vm(); auto& vm = this->vm();
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
// 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. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// NOTE: 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.
VERIFY(is_function()); VERIFY(is_function());
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
if (m_is_revoked)
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 5. Let trap be ? GetMethod(handler, "apply"). // 5. Let trap be ? GetMethod(handler, "apply").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.apply)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.apply));
@ -854,19 +830,16 @@ ThrowCompletionOr<GC::Ref<Object>> ProxyObject::internal_construct(ReadonlySpan<
auto& vm = this->vm(); auto& vm = this->vm();
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
// 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. // 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// NOTE: 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.
VERIFY(is_function()); VERIFY(is_function());
// 1. Let handler be O.[[ProxyHandler]].
// 2. If handler is null, throw a TypeError exception.
if (m_is_revoked)
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// 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"). // 6. Let trap be ? GetMethod(handler, "construct").
auto trap = TRY(Value(m_handler).get_method(vm, vm.names.construct)); auto trap = TRY(Value(m_handler).get_method(vm, vm.names.construct));
@ -890,6 +863,22 @@ ThrowCompletionOr<GC::Ref<Object>> ProxyObject::internal_construct(ReadonlySpan<
return new_object.as_object(); return new_object.as_object();
} }
// 10.5.14 ValidateNonRevokedProxy ( proxy )
ThrowCompletionOr<void> ProxyObject::validate_non_revoked_proxy() const
{
auto& vm = this->vm();
// FIXME: The spec expects us to model a revoked proxy by having ProxyTarget and ProxyHandler be nullable.
// 1. If proxy.[[ProxyTarget]] is null, throw a TypeError exception.
// 2. Assert: proxy.[[ProxyHandler]] is not null.
if (m_is_revoked)
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// 3. Return unused.
return {};
}
void ProxyObject::visit_edges(Cell::Visitor& visitor) void ProxyObject::visit_edges(Cell::Visitor& visitor)
{ {
Base::visit_edges(visitor); Base::visit_edges(visitor);

View file

@ -44,6 +44,7 @@ public:
virtual ThrowCompletionOr<GC::RootVector<Value>> internal_own_property_keys() const override; virtual ThrowCompletionOr<GC::RootVector<Value>> internal_own_property_keys() const override;
virtual ThrowCompletionOr<Value> internal_call(Value this_argument, ReadonlySpan<Value> arguments_list) override; virtual ThrowCompletionOr<Value> internal_call(Value this_argument, ReadonlySpan<Value> arguments_list) override;
virtual ThrowCompletionOr<GC::Ref<Object>> internal_construct(ReadonlySpan<Value> arguments_list, FunctionObject& new_target) override; virtual ThrowCompletionOr<GC::Ref<Object>> internal_construct(ReadonlySpan<Value> arguments_list, FunctionObject& new_target) override;
ThrowCompletionOr<void> validate_non_revoked_proxy() const;
private: private:
ProxyObject(Object& target, Object& handler, Object& prototype); ProxyObject(Object& target, Object& handler, Object& prototype);

View file

@ -233,18 +233,16 @@ ThrowCompletionOr<bool> Value::is_array(VM& vm) const
return true; return true;
// 3. If argument is a Proxy exotic object, then // 3. If argument is a Proxy exotic object, then
if (is<ProxyObject>(object)) { if (auto const* proxy = as_if<ProxyObject>(object)) {
auto const& proxy = static_cast<ProxyObject const&>(object);
// a. If argument.[[ProxyHandler]] is null, throw a TypeError exception. // a. Perform ? ValidateNonRevokedProxy(argument).
if (proxy.is_revoked()) TRY(proxy->validate_non_revoked_proxy());
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// b. Let target be argument.[[ProxyTarget]]. // b. Let proxyTarget be argument.[[ProxyTarget]].
auto const& target = proxy.target(); auto& proxy_target = proxy->target();
// c. Return ? IsArray(target). // c. Return ? IsArray(proxyTarget).
return Value(&target).is_array(vm); return Value(&proxy_target).is_array(vm);
} }
// 4. Return false. // 4. Return false.