1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 19:37:36 +00:00

LibWeb: Convert throw_dom_exception_if_needed() to ThrowCompletionOr

This changes Web::Bindings::throw_dom_exception_if_needed() to return a
JS::ThrowCompletionOr instead of an Optional. This allows callers to
wrap the invocation with a TRY() macro instead of making a follow-up
call to should_return_empty(). Further, this removes all invocations to
vm.exception() in the generated bindings.
This commit is contained in:
Timothy Flynn 2021-10-31 13:07:52 -04:00 committed by Linus Groh
parent e3b7c305bc
commit 95e492de59
4 changed files with 84 additions and 168 deletions

View file

@ -1414,21 +1414,14 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
if (is_static_function == StaticFunction::No) { if (is_static_function == StaticFunction::No) {
function_generator.append(R"~~~( function_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->@function.cpp_name@(@.arguments@); }); [[maybe_unused]] auto retval = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl->@function.cpp_name@(@.arguments@); }));
)~~~"); )~~~");
} else { } else {
function_generator.append(R"~~~( function_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return @interface_fully_qualified_name@::@function.cpp_name@(@.arguments@); }); [[maybe_unused]] auto retval = TRY(throw_dom_exception_if_needed(global_object, [&] { return @interface_fully_qualified_name@::@function.cpp_name@(@.arguments@); }));
)~~~"); )~~~");
} }
function_generator.append(R"~~~(
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
[[maybe_unused]] auto retval = result.release_value();
)~~~");
generate_return_statement(generator, *function.return_type); generate_return_statement(generator, *function.return_type);
function_generator.append(R"~~~( function_generator.append(R"~~~(
@ -1558,8 +1551,8 @@ private:
if (interface.is_legacy_platform_object()) { if (interface.is_legacy_platform_object()) {
generator.append(R"~~~( generator.append(R"~~~(
bool is_named_property_exposed_on_object(JS::PropertyKey const&) const; bool is_named_property_exposed_on_object(JS::PropertyKey const&) const;
Optional<JS::PropertyDescriptor> legacy_platform_object_get_own_property_for_get_own_property_slot(JS::PropertyKey const&) const; JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> legacy_platform_object_get_own_property_for_get_own_property_slot(JS::PropertyKey const&) const;
Optional<JS::PropertyDescriptor> legacy_platform_object_get_own_property_for_set_slot(JS::PropertyKey const&) const; JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> legacy_platform_object_get_own_property_for_set_slot(JS::PropertyKey const&) const;
)~~~"); )~~~");
} }
@ -1807,12 +1800,11 @@ bool @class_name@::is_named_property_exposed_on_object(JS::PropertyKey const& pr
get_own_property_generator.set("internal_method"sv, for_which_internal_method); get_own_property_generator.set("internal_method"sv, for_which_internal_method);
get_own_property_generator.append(R"~~~( get_own_property_generator.append(R"~~~(
Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_property_for_@internal_method@_slot(JS::PropertyKey const& property_name) const JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> @class_name@::legacy_platform_object_get_own_property_for_@internal_method@_slot(JS::PropertyKey const& property_name) const
{ {
)~~~"); )~~~");
get_own_property_generator.append(R"~~~( get_own_property_generator.append(R"~~~(
[[maybe_unused]] auto& vm = this->vm();
[[maybe_unused]] auto& global_object = this->global_object(); [[maybe_unused]] auto& global_object = this->global_object();
)~~~"); )~~~");
@ -1834,7 +1826,7 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
// 3. If operation was defined without an identifier, then set value to the result of performing the steps listed in the interface description to determine the value of an indexed property with index as the index. // 3. If operation was defined without an identifier, then set value to the result of performing the steps listed in the interface description to determine the value of an indexed property with index as the index.
if (interface.indexed_property_getter->name.is_empty()) { if (interface.indexed_property_getter->name.is_empty()) {
get_own_property_generator.append(R"~~~( get_own_property_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl().determine_value_of_indexed_property(index); }); auto value = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().determine_value_of_indexed_property(index); }));
)~~~"); )~~~");
} }
@ -1845,16 +1837,11 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.indexed_property_getter->name.to_snakecase())); function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.indexed_property_getter->name.to_snakecase()));
function_scoped_generator.append(R"~~~( function_scoped_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl().@function.cpp_name@(index); }); auto value = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().@function.cpp_name@(index); }));
)~~~"); )~~~");
} }
get_own_property_generator.append(R"~~~( get_own_property_generator.append(R"~~~(
if (should_return_empty(result))
return {};
auto value = result.release_value();
// 5. Let desc be a newly created Property Descriptor with no fields. // 5. Let desc be a newly created Property Descriptor with no fields.
JS::PropertyDescriptor descriptor; JS::PropertyDescriptor descriptor;
@ -1885,7 +1872,7 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
// 3. Set ignoreNamedProps to true. // 3. Set ignoreNamedProps to true.
// NOTE: To reduce complexity of WrapperGenerator, this just returns early instead of keeping track of another variable. // NOTE: To reduce complexity of WrapperGenerator, this just returns early instead of keeping track of another variable.
return TRY_OR_DISCARD(Object::internal_get_own_property(property_name)); return TRY(Object::internal_get_own_property(property_name));
} }
)~~~"); )~~~");
} }
@ -1905,7 +1892,7 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
// 3. If operation was defined without an identifier, then set value to the result of performing the steps listed in the interface description to determine the value of a named property with P as the name. // 3. If operation was defined without an identifier, then set value to the result of performing the steps listed in the interface description to determine the value of a named property with P as the name.
if (interface.named_property_getter->name.is_empty()) { if (interface.named_property_getter->name.is_empty()) {
get_own_property_generator.append(R"~~~( get_own_property_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl().determine_value_of_named_property(property_name_string); }); auto value = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().determine_value_of_named_property(property_name_string); }));
)~~~"); )~~~");
} }
@ -1915,16 +1902,11 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.named_property_getter->name.to_snakecase())); function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.named_property_getter->name.to_snakecase()));
function_scoped_generator.append(R"~~~( function_scoped_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl().@function.cpp_name@(property_name_string); }); auto value = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().@function.cpp_name@(property_name_string); }));
)~~~"); )~~~");
} }
get_own_property_generator.append(R"~~~( get_own_property_generator.append(R"~~~(
if (should_return_empty(result))
return {};
auto value = result.release_value();
// 5. Let desc be a newly created Property Descriptor with no fields. // 5. Let desc be a newly created Property Descriptor with no fields.
JS::PropertyDescriptor descriptor; JS::PropertyDescriptor descriptor;
@ -1966,7 +1948,7 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
// 3. Return OrdinaryGetOwnProperty(O, P). // 3. Return OrdinaryGetOwnProperty(O, P).
get_own_property_generator.append(R"~~~( get_own_property_generator.append(R"~~~(
return TRY_OR_DISCARD(Object::internal_get_own_property(property_name)); return TRY(Object::internal_get_own_property(property_name));
} }
)~~~"); )~~~");
}; };
@ -1985,8 +1967,6 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
static JS::ThrowCompletionOr<void> invoke_named_property_setter(JS::GlobalObject& global_object, @fully_qualified_name@& impl, String const& property_name, JS::Value value) static JS::ThrowCompletionOr<void> invoke_named_property_setter(JS::GlobalObject& global_object, @fully_qualified_name@& impl, String const& property_name, JS::Value value)
{ {
auto& vm = global_object.vm();
// 1. Let creating be true if P is not a supported property name, and false otherwise. // 1. Let creating be true if P is not a supported property name, and false otherwise.
// NOTE: This is in it's own variable to enforce the type. // NOTE: This is in it's own variable to enforce the type.
// FIXME: Can this throw? // FIXME: Can this throw?
@ -2005,14 +1985,10 @@ static JS::ThrowCompletionOr<void> invoke_named_property_setter(JS::GlobalObject
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
if (creating) { if (creating) {
// 5.1. If creating is true, then perform the steps listed in the interface description to set the value of a new named property with P as the name and value as the value. // 5.1. If creating is true, then perform the steps listed in the interface description to set the value of a new named property with P as the name and value as the value.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.set_value_of_new_named_property(property_name, converted_value); }); TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_new_named_property(property_name, converted_value); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
} else { } else {
// 5.2 Otherwise, creating is false. Perform the steps listed in the interface description to set the value of an existing named property with P as the name and value as the value. // 5.2 Otherwise, creating is false. Perform the steps listed in the interface description to set the value of an existing named property with P as the name and value as the value.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.set_value_of_existing_named_property(property_name, converted_value); }); TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_existing_named_property(property_name, converted_value); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
} }
)~~~"); )~~~");
} else { } else {
@ -2022,9 +1998,7 @@ static JS::ThrowCompletionOr<void> invoke_named_property_setter(JS::GlobalObject
function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.named_property_setter->name.to_snakecase())); function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.named_property_setter->name.to_snakecase()));
function_scoped_generator.append(R"~~~( function_scoped_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.@function.cpp_name@(property_name, converted_value); }); TRY(throw_dom_exception_if_needed(global_object, [&] { impl.@function.cpp_name@(property_name, converted_value); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
)~~~"); )~~~");
} }
@ -2042,8 +2016,6 @@ static JS::ThrowCompletionOr<void> invoke_named_property_setter(JS::GlobalObject
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
static JS::ThrowCompletionOr<void> invoke_indexed_property_setter(JS::GlobalObject& global_object, @fully_qualified_name@& impl, JS::PropertyKey const& property_name, JS::Value value) static JS::ThrowCompletionOr<void> invoke_indexed_property_setter(JS::GlobalObject& global_object, @fully_qualified_name@& impl, JS::PropertyKey const& property_name, JS::Value value)
{ {
auto& vm = global_object.vm();
// 1. Let index be the result of calling ToUint32(P). // 1. Let index be the result of calling ToUint32(P).
u32 index = property_name.as_number(); u32 index = property_name.as_number();
@ -2064,14 +2036,10 @@ static JS::ThrowCompletionOr<void> invoke_indexed_property_setter(JS::GlobalObje
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
if (creating) { if (creating) {
// 6.1 If creating is true, then perform the steps listed in the interface description to set the value of a new indexed property with index as the index and value as the value. // 6.1 If creating is true, then perform the steps listed in the interface description to set the value of a new indexed property with index as the index and value as the value.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.set_value_of_new_indexed_property(index, converted_value); }); TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_new_indexed_property(index, converted_value); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
} else { } else {
// 6.2 Otherwise, creating is false. Perform the steps listed in the interface description to set the value of an existing indexed property with index as the index and value as the value. // 6.2 Otherwise, creating is false. Perform the steps listed in the interface description to set the value of an existing indexed property with index as the index and value as the value.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.set_value_of_existing_indexed_property(index, converted_value); }); TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_existing_indexed_property(index, converted_value); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
} }
)~~~"); )~~~");
} else { } else {
@ -2081,9 +2049,7 @@ static JS::ThrowCompletionOr<void> invoke_indexed_property_setter(JS::GlobalObje
function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.indexed_property_setter->name.to_snakecase())); function_scoped_generator.set("function.cpp_name", make_input_acceptable_cpp(interface.indexed_property_setter->name.to_snakecase()));
function_scoped_generator.append(R"~~~( function_scoped_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.@function.cpp_name@(index, converted_value); }); TRY(throw_dom_exception_if_needed(global_object, [&] { impl.@function.cpp_name@(index, converted_value); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
)~~~"); )~~~");
} }
@ -2099,7 +2065,7 @@ static JS::ThrowCompletionOr<void> invoke_indexed_property_setter(JS::GlobalObje
JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> @class_name@::internal_get_own_property(JS::PropertyKey const& property_name) const JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> @class_name@::internal_get_own_property(JS::PropertyKey const& property_name) const
{ {
// 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false). // 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
return legacy_platform_object_get_own_property_for_get_own_property_slot(property_name); return TRY(legacy_platform_object_get_own_property_for_get_own_property_slot(property_name));
} }
)~~~"); )~~~");
@ -2107,7 +2073,6 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> @class_name@::internal_g
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
JS::ThrowCompletionOr<bool> @class_name@::internal_set(JS::PropertyKey const& property_name, JS::Value value, JS::Value receiver) JS::ThrowCompletionOr<bool> @class_name@::internal_set(JS::PropertyKey const& property_name, JS::Value value, JS::Value receiver)
{ {
auto& vm = this->vm();
[[maybe_unused]] auto& global_object = this->global_object(); [[maybe_unused]] auto& global_object = this->global_object();
)~~~"); )~~~");
@ -2153,9 +2118,7 @@ JS::ThrowCompletionOr<bool> @class_name@::internal_set(JS::PropertyKey const& pr
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
// 2. Let ownDesc be LegacyPlatformObjectGetOwnProperty(O, P, true). // 2. Let ownDesc be LegacyPlatformObjectGetOwnProperty(O, P, true).
auto own_descriptor = legacy_platform_object_get_own_property_for_set_slot(property_name); auto own_descriptor = TRY(legacy_platform_object_get_own_property_for_set_slot(property_name));
if (auto* exception = vm.exception())
return JS::throw_completion(exception->value());
// 3. Perform ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc). // 3. Perform ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).
// NOTE: The spec says "perform" instead of "return", meaning nothing will be returned on this path according to the spec, which isn't possible to do. // NOTE: The spec says "perform" instead of "return", meaning nothing will be returned on this path according to the spec, which isn't possible to do.
@ -2288,7 +2251,6 @@ JS::ThrowCompletionOr<bool> @class_name@::internal_define_own_property(JS::Prope
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
JS::ThrowCompletionOr<bool> @class_name@::internal_delete(JS::PropertyKey const& property_name) JS::ThrowCompletionOr<bool> @class_name@::internal_delete(JS::PropertyKey const& property_name)
{ {
[[maybe_unused]] auto& vm = this->vm();
auto& global_object = this->global_object(); auto& global_object = this->global_object();
)~~~"); )~~~");
@ -2335,12 +2297,7 @@ JS::ThrowCompletionOr<bool> @class_name@::internal_delete(JS::PropertyKey const&
if (interface.named_property_deleter->name.is_empty()) { if (interface.named_property_deleter->name.is_empty()) {
scoped_generator.append(R"~~~( scoped_generator.append(R"~~~(
// 1. Perform the steps listed in the interface description to delete an existing named property with P as the name. // 1. Perform the steps listed in the interface description to delete an existing named property with P as the name.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl().delete_existing_named_property(property_name_string); }); bool succeeded = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().delete_existing_named_property(property_name_string); }));
// FIXME: Make this nicer for ThrowCompletionOr.
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
bool succeeded = result.release_value();
// 2. If the steps indicated that the deletion failed, then return false. // 2. If the steps indicated that the deletion failed, then return false.
if (!succeeded) if (!succeeded)
@ -2353,17 +2310,13 @@ JS::ThrowCompletionOr<bool> @class_name@::internal_delete(JS::PropertyKey const&
function_scoped_generator.append(R"~~~( function_scoped_generator.append(R"~~~(
// 1. Perform method steps of operation with O as this and « P » as the argument values. // 1. Perform method steps of operation with O as this and « P » as the argument values.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl().@function.cpp_name@(property_name_string); }); [[maybe_unused]] auto result = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().@function.cpp_name@(property_name_string); }));
// FIXME: Make this nicer for ThrowCompletionOr.
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
)~~~"); )~~~");
// 2. If operation was declared with a return type of boolean and the steps returned false, then return false. // 2. If operation was declared with a return type of boolean and the steps returned false, then return false.
if (interface.named_property_deleter->return_type->name == "boolean") { if (interface.named_property_deleter->return_type->name == "boolean") {
function_scoped_generator.append(R"~~~( function_scoped_generator.append(R"~~~(
bool succeeded = result.release_value(); if (!result)
if (!succeeded)
return false; return false;
)~~~"); )~~~");
} }
@ -2632,17 +2585,15 @@ JS::ThrowCompletionOr<JS::Object*> @constructor_class@::construct(FunctionObject
generator.set(".constructor_arguments", arguments_builder.string_view()); generator.set(".constructor_arguments", arguments_builder.string_view());
generator.append(R"~~~( generator.append(R"~~~(
auto impl = throw_dom_exception_if_needed(vm, global_object, [&] { return @fully_qualified_name@::create_with_global_object(window, @.constructor_arguments@); }); auto impl = TRY(throw_dom_exception_if_needed(global_object, [&] { return @fully_qualified_name@::create_with_global_object(window, @.constructor_arguments@); }));
)~~~"); )~~~");
} else { } else {
generator.append(R"~~~( generator.append(R"~~~(
auto impl = throw_dom_exception_if_needed(vm, global_object, [&] { return @fully_qualified_name@::create_with_global_object(window); }); auto impl = TRY(throw_dom_exception_if_needed(global_object, [&] { return @fully_qualified_name@::create_with_global_object(window); }));
)~~~"); )~~~");
} }
generator.append(R"~~~( generator.append(R"~~~(
if (should_return_empty(impl)) return wrap(global_object, *impl);
return JS::throw_completion(vm.exception()->value());
return wrap(global_object, *impl.release_value());
)~~~"); )~~~");
} else { } else {
// Multiple constructor overloads - can't do that yet. // Multiple constructor overloads - can't do that yet.
@ -3197,10 +3148,7 @@ JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@attribute.setter_callback@)
} }
} else { } else {
attribute_generator.append(R"~~~( attribute_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->set_@attribute.name:snakecase@(cpp_value); }); TRY(throw_dom_exception_if_needed(global_object, [&] { return impl->set_@attribute.name:snakecase@(cpp_value); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
)~~~"); )~~~");
} }
@ -3234,12 +3182,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_string)
)~~~"); )~~~");
} else { } else {
stringifier_generator.append(R"~~~( stringifier_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->to_string(); }); auto retval = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl->to_string(); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
auto retval = result.release_value();
)~~~"); )~~~");
} }
stringifier_generator.append(R"~~~( stringifier_generator.append(R"~~~(
@ -3583,12 +3526,7 @@ static JS::ThrowCompletionOr<@fully_qualified_name@*> impl_from(JS::VM& vm, JS::
JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::next) JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::next)
{ {
auto* impl = TRY(impl_from(vm, global_object)); auto* impl = TRY(impl_from(vm, global_object));
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->next(); }); return TRY(throw_dom_exception_if_needed(global_object, [&] { return impl->next(); }));
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
return result.release_value();
} }
} // namespace Web::Bindings } // namespace Web::Bindings

View file

@ -20,19 +20,6 @@ constexpr bool IsExceptionOr = false;
template<typename T> template<typename T>
constexpr bool IsExceptionOr<DOM::ExceptionOr<T>> = true; constexpr bool IsExceptionOr<DOM::ExceptionOr<T>> = true;
template<typename T>
ALWAYS_INLINE bool throw_dom_exception(JS::VM& vm, JS::GlobalObject& global_object, DOM::ExceptionOr<T>& result)
{
if (result.is_exception()) {
result.materialized_exception(global_object)
.visit(
[&](NonnullRefPtr<DOM::DOMException> dom_exception) { vm.throw_exception(global_object, DOMExceptionWrapper::create(global_object, move(dom_exception))); },
[&](auto* js_exception) { vm.throw_exception(global_object, js_exception); });
return true;
}
return false;
}
namespace Detail { namespace Detail {
template<typename T> template<typename T>
@ -60,22 +47,47 @@ struct ExtractExceptionOrValueType<DOM::ExceptionOr<void>> {
using Type = JS::Value; using Type = JS::Value;
}; };
ALWAYS_INLINE JS::Completion dom_exception_to_throw_completion(auto&& global_object, auto&& exception)
{
auto& vm = global_object.vm();
return exception.visit(
[&](DOM::SimpleException const& exception) {
switch (exception.type) {
#define E(x) \
case DOM::SimpleExceptionType::x: \
return vm.template throw_completion<JS::x>(global_object, exception.message);
ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E)
#undef E
default:
VERIFY_NOT_REACHED();
}
},
[&](NonnullRefPtr<DOM::DOMException> exception) {
return vm.template throw_completion<DOMExceptionWrapper>(global_object, move(exception));
});
}
} }
template<typename T> template<typename T>
using ExtractExceptionOrValueType = typename Detail::ExtractExceptionOrValueType<T>::Type; using ExtractExceptionOrValueType = typename Detail::ExtractExceptionOrValueType<T>::Type;
// Return type depends on the return type of 'fn' (when invoked with no args): // Return type depends on the return type of 'fn' (when invoked with no args):
// void or ExceptionOr<void>: Optional<JS::Value>, always returns JS::js_undefined() // void or ExceptionOr<void>: JS::ThrowCompletionOr<JS::Value>, always returns JS::js_undefined()
// ExceptionOr<T>: Optional<T> // ExceptionOr<T>: JS::ThrowCompletionOr<T>
// T: Optional<T> // T: JS::ThrowCompletionOr<T>
template<typename F, typename T = decltype(declval<F>()()), typename Ret = Conditional<!IsExceptionOr<T> && !IsVoid<T>, T, ExtractExceptionOrValueType<T>>> template<typename F, typename T = decltype(declval<F>()()), typename Ret = Conditional<!IsExceptionOr<T> && !IsVoid<T>, T, ExtractExceptionOrValueType<T>>>
Optional<Ret> throw_dom_exception_if_needed(auto&& vm, auto&& global_object, F&& fn) JS::ThrowCompletionOr<Ret> throw_dom_exception_if_needed(auto&& global_object, F&& fn)
{ {
if constexpr (IsExceptionOr<T>) { if constexpr (IsExceptionOr<T>) {
auto&& result = fn(); auto&& result = fn();
if (throw_dom_exception(vm, global_object, result))
return {}; if (result.is_exception())
return Detail::dom_exception_to_throw_completion(global_object, result.exception());
if constexpr (requires(T v) { v.value(); }) if constexpr (requires(T v) { v.value(); })
return result.value(); return result.value();
else else
@ -88,12 +100,4 @@ Optional<Ret> throw_dom_exception_if_needed(auto&& vm, auto&& global_object, F&&
} }
} }
template<typename T>
bool should_return_empty(const Optional<T>& value)
{
if constexpr (IsSame<JS::Value, T>)
return !value.has_value() || value.value().is_empty();
return !value.has_value();
}
} }

View file

@ -636,33 +636,31 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::screen_y_getter)
return JS::Value(impl->screen_y()); return JS::Value(impl->screen_y());
} }
#define __ENUMERATE(attribute, event_name) \ #define __ENUMERATE(attribute, event_name) \
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \ JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \
{ \ { \
auto* impl = TRY(impl_from(vm, global_object)); \ auto* impl = TRY(impl_from(vm, global_object)); \
auto retval = impl->attribute(); \ auto retval = impl->attribute(); \
if (retval.callback.is_null()) \ if (retval.callback.is_null()) \
return JS::js_null(); \ return JS::js_null(); \
return retval.callback.cell(); \ return retval.callback.cell(); \
} \ } \
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_setter) \ JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_setter) \
{ \ { \
auto* impl = TRY(impl_from(vm, global_object)); \ auto* impl = TRY(impl_from(vm, global_object)); \
auto value = vm.argument(0); \ auto value = vm.argument(0); \
HTML::EventHandler cpp_value; \ HTML::EventHandler cpp_value; \
if (value.is_function()) { \ if (value.is_function()) { \
cpp_value.callback = JS::make_handle(&value.as_function()); \ cpp_value.callback = JS::make_handle(&value.as_function()); \
} else if (value.is_string()) { \ } else if (value.is_string()) { \
cpp_value.string = value.as_string().string(); \ cpp_value.string = value.as_string().string(); \
} else { \ } else { \
return JS::js_undefined(); \ return JS::js_undefined(); \
} \ } \
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { \ TRY(throw_dom_exception_if_needed(global_object, [&] { \
return impl->set_##attribute(cpp_value); \ return impl->set_##attribute(cpp_value); \
}); \ })); \
if (should_return_empty(result)) \ return JS::js_undefined(); \
return JS::throw_completion(vm.exception()->value()); \
return JS::js_undefined(); \
} }
ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE) ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE #undef __ENUMERATE

View file

@ -80,30 +80,6 @@ public:
return m_exception.template downcast<SimpleException, NonnullRefPtr<DOMException>>(); return m_exception.template downcast<SimpleException, NonnullRefPtr<DOMException>>();
} }
auto materialized_exception(JS::GlobalObject& global_object) const
{
#define E(x) JS::x*,
using ResultType = Variant<ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E) NonnullRefPtr<DOMException>>;
#undef E
return m_exception.visit(
[&](SimpleException& exception) -> ResultType {
switch (exception.type) {
#define E(x) \
case SimpleExceptionType::x: \
return JS::x::create(global_object, exception.message);
ENUMERATE_SIMPLE_WEBIDL_EXCEPTION_TYPES(E)
#undef E
default:
VERIFY_NOT_REACHED();
}
},
[&](NonnullRefPtr<DOMException> const& exception) -> ResultType { return exception; },
[](Empty) -> ResultType { VERIFY_NOT_REACHED(); });
}
bool is_exception() const bool is_exception() const
{ {
return !m_exception.template has<Empty>(); return !m_exception.template has<Empty>();