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
if (is<BoundFunction>(function)) {
auto& bound_function = static_cast<BoundFunction const&>(function);
if (auto const* bound_function = as_if<BoundFunction>(function)) {
// a. Let boundTargetFunction be obj.[[BoundTargetFunction]].
auto& bound_target_function = bound_function->bound_target_function();
// a. Let target be obj.[[BoundTargetFunction]].
auto& target = bound_function.bound_target_function();
// b. Return ? GetFunctionRealm(target).
return get_function_realm(vm, target);
// b. Return ? GetFunctionRealm(boundTargetFunction).
return get_function_realm(vm, bound_target_function);
}
// 3. If obj is a Proxy exotic object, then
if (is<ProxyObject>(function)) {
auto& proxy = static_cast<ProxyObject const&>(function);
// a. If obj.[[ProxyHandler]] is null, throw a TypeError exception.
if (proxy.is_revoked())
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
if (auto const* proxy = as_if<ProxyObject>(function)) {
// a. a. Perform ? ValidateNonRevokedProxy(obj).
TRY(proxy->validate_non_revoked_proxy());
// 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());
// d. Return ? GetFunctionRealm(proxyTarget).
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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 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
@ -490,14 +476,12 @@ ThrowCompletionOr<Value> ProxyObject::internal_get(PropertyKey const& property_k
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.
if (m_is_revoked)
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 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
@ -560,14 +544,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_set(PropertyKey const& property_ke
VERIFY(!value.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.
if (m_is_revoked)
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 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
@ -631,14 +613,12 @@ ThrowCompletionOr<bool> ProxyObject::internal_delete(PropertyKey const& property
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.
if (m_is_revoked)
return vm.throw_completion<TypeError>(ErrorType::ProxyRevoked);
// 3. Assert: Type(handler) is Object.
// 4. Let target be O.[[ProxyTarget]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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();
// 1. Let handler be O.[[ProxyHandler]].
// 1. Perform ? ValidateNonRevokedProxy(O).
TRY(validate_non_revoked_proxy());
// 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]].
// 2. Let target be O.[[ProxyTarget]].
// 3. Let handler be O.[[ProxyHandler]].
// 4. Assert: handler is an Object.
// 5. Let trap be ? GetMethod(handler, "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& 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());
// 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").
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& 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());
// 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").
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();
}
// 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)
{
Base::visit_edges(visitor);

View file

@ -44,6 +44,7 @@ public:
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<GC::Ref<Object>> internal_construct(ReadonlySpan<Value> arguments_list, FunctionObject& new_target) override;
ThrowCompletionOr<void> validate_non_revoked_proxy() const;
private:
ProxyObject(Object& target, Object& handler, Object& prototype);

View file

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