1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 17:47:44 +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) {
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 {
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);
function_generator.append(R"~~~(
@ -1558,8 +1551,8 @@ private:
if (interface.is_legacy_platform_object()) {
generator.append(R"~~~(
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;
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_get_own_property_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.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"~~~(
[[maybe_unused]] auto& vm = this->vm();
[[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.
if (interface.indexed_property_getter->name.is_empty()) {
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.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"~~~(
if (should_return_empty(result))
return {};
auto value = result.release_value();
// 5. Let desc be a newly created Property Descriptor with no fields.
JS::PropertyDescriptor descriptor;
@ -1885,7 +1872,7 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
// 3. Set ignoreNamedProps to true.
// 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.
if (interface.named_property_getter->name.is_empty()) {
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.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"~~~(
if (should_return_empty(result))
return {};
auto value = result.release_value();
// 5. Let desc be a newly created Property Descriptor with no fields.
JS::PropertyDescriptor descriptor;
@ -1966,7 +1948,7 @@ Optional<JS::PropertyDescriptor> @class_name@::legacy_platform_object_get_own_pr
// 3. Return OrdinaryGetOwnProperty(O, P).
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"~~~(
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.
// NOTE: This is in it's own variable to enforce the type.
// FIXME: Can this throw?
@ -2005,14 +1985,10 @@ static JS::ThrowCompletionOr<void> invoke_named_property_setter(JS::GlobalObject
scoped_generator.append(R"~~~(
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.
auto result = throw_dom_exception_if_needed(vm, 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());
TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_new_named_property(property_name, converted_value); }));
} 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.
auto result = throw_dom_exception_if_needed(vm, 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());
TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_existing_named_property(property_name, converted_value); }));
}
)~~~");
} 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.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.@function.cpp_name@(property_name, converted_value); });
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
TRY(throw_dom_exception_if_needed(global_object, [&] { impl.@function.cpp_name@(property_name, converted_value); }));
)~~~");
}
@ -2042,8 +2016,6 @@ static JS::ThrowCompletionOr<void> invoke_named_property_setter(JS::GlobalObject
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)
{
auto& vm = global_object.vm();
// 1. Let index be the result of calling ToUint32(P).
u32 index = property_name.as_number();
@ -2064,14 +2036,10 @@ static JS::ThrowCompletionOr<void> invoke_indexed_property_setter(JS::GlobalObje
scoped_generator.append(R"~~~(
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.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.set_value_of_new_indexed_property(index, converted_value); });
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_new_indexed_property(index, converted_value); }));
} 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.
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.set_value_of_existing_indexed_property(index, converted_value); });
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
TRY(throw_dom_exception_if_needed(global_object, [&] { impl.set_value_of_existing_indexed_property(index, converted_value); }));
}
)~~~");
} 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.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { impl.@function.cpp_name@(index, converted_value); });
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
TRY(throw_dom_exception_if_needed(global_object, [&] { impl.@function.cpp_name@(index, converted_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
{
// 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"~~~(
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();
)~~~");
@ -2153,9 +2118,7 @@ JS::ThrowCompletionOr<bool> @class_name@::internal_set(JS::PropertyKey const& pr
scoped_generator.append(R"~~~(
// 2. Let ownDesc be LegacyPlatformObjectGetOwnProperty(O, P, true).
auto own_descriptor = legacy_platform_object_get_own_property_for_set_slot(property_name);
if (auto* exception = vm.exception())
return JS::throw_completion(exception->value());
auto own_descriptor = TRY(legacy_platform_object_get_own_property_for_set_slot(property_name));
// 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.
@ -2288,7 +2251,6 @@ JS::ThrowCompletionOr<bool> @class_name@::internal_define_own_property(JS::Prope
scoped_generator.append(R"~~~(
JS::ThrowCompletionOr<bool> @class_name@::internal_delete(JS::PropertyKey const& property_name)
{
[[maybe_unused]] auto& vm = this->vm();
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()) {
scoped_generator.append(R"~~~(
// 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); });
// FIXME: Make this nicer for ThrowCompletionOr.
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
bool succeeded = result.release_value();
bool succeeded = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().delete_existing_named_property(property_name_string); }));
// 2. If the steps indicated that the deletion failed, then return false.
if (!succeeded)
@ -2353,17 +2310,13 @@ JS::ThrowCompletionOr<bool> @class_name@::internal_delete(JS::PropertyKey const&
function_scoped_generator.append(R"~~~(
// 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); });
// FIXME: Make this nicer for ThrowCompletionOr.
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
[[maybe_unused]] auto result = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl().@function.cpp_name@(property_name_string); }));
)~~~");
// 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") {
function_scoped_generator.append(R"~~~(
bool succeeded = result.release_value();
if (!succeeded)
if (!result)
return false;
)~~~");
}
@ -2632,17 +2585,15 @@ JS::ThrowCompletionOr<JS::Object*> @constructor_class@::construct(FunctionObject
generator.set(".constructor_arguments", arguments_builder.string_view());
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 {
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"~~~(
if (should_return_empty(impl))
return JS::throw_completion(vm.exception()->value());
return wrap(global_object, *impl.release_value());
return wrap(global_object, *impl);
)~~~");
} else {
// Multiple constructor overloads - can't do that yet.
@ -3197,10 +3148,7 @@ JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@attribute.setter_callback@)
}
} else {
attribute_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->set_@attribute.name:snakecase@(cpp_value); });
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
TRY(throw_dom_exception_if_needed(global_object, [&] { return impl->set_@attribute.name:snakecase@(cpp_value); }));
)~~~");
}
@ -3234,12 +3182,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_string)
)~~~");
} else {
stringifier_generator.append(R"~~~(
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->to_string(); });
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
auto retval = result.release_value();
auto retval = TRY(throw_dom_exception_if_needed(global_object, [&] { return impl->to_string(); }));
)~~~");
}
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)
{
auto* impl = TRY(impl_from(vm, global_object));
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->next(); });
if (should_return_empty(result))
return JS::throw_completion(vm.exception()->value());
return result.release_value();
return TRY(throw_dom_exception_if_needed(global_object, [&] { return impl->next(); }));
}
} // namespace Web::Bindings