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

View file

@ -20,19 +20,6 @@ constexpr bool IsExceptionOr = false;
template<typename T>
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 {
template<typename T>
@ -60,22 +47,47 @@ struct ExtractExceptionOrValueType<DOM::ExceptionOr<void>> {
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>
using ExtractExceptionOrValueType = typename Detail::ExtractExceptionOrValueType<T>::Type;
// 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()
// ExceptionOr<T>: Optional<T>
// T: Optional<T>
// void or ExceptionOr<void>: JS::ThrowCompletionOr<JS::Value>, always returns JS::js_undefined()
// ExceptionOr<T>: JS::ThrowCompletionOr<T>
// T: JS::ThrowCompletionOr<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>) {
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(); })
return result.value();
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());
}
#define __ENUMERATE(attribute, event_name) \
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \
{ \
auto* impl = TRY(impl_from(vm, global_object)); \
auto retval = impl->attribute(); \
if (retval.callback.is_null()) \
return JS::js_null(); \
return retval.callback.cell(); \
} \
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_setter) \
{ \
auto* impl = TRY(impl_from(vm, global_object)); \
auto value = vm.argument(0); \
HTML::EventHandler cpp_value; \
if (value.is_function()) { \
cpp_value.callback = JS::make_handle(&value.as_function()); \
} else if (value.is_string()) { \
cpp_value.string = value.as_string().string(); \
} else { \
return JS::js_undefined(); \
} \
auto result = throw_dom_exception_if_needed(vm, global_object, [&] { \
return impl->set_##attribute(cpp_value); \
}); \
if (should_return_empty(result)) \
return JS::throw_completion(vm.exception()->value()); \
return JS::js_undefined(); \
#define __ENUMERATE(attribute, event_name) \
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_getter) \
{ \
auto* impl = TRY(impl_from(vm, global_object)); \
auto retval = impl->attribute(); \
if (retval.callback.is_null()) \
return JS::js_null(); \
return retval.callback.cell(); \
} \
JS_DEFINE_NATIVE_FUNCTION(WindowObject::attribute##_setter) \
{ \
auto* impl = TRY(impl_from(vm, global_object)); \
auto value = vm.argument(0); \
HTML::EventHandler cpp_value; \
if (value.is_function()) { \
cpp_value.callback = JS::make_handle(&value.as_function()); \
} else if (value.is_string()) { \
cpp_value.string = value.as_string().string(); \
} else { \
return JS::js_undefined(); \
} \
TRY(throw_dom_exception_if_needed(global_object, [&] { \
return impl->set_##attribute(cpp_value); \
})); \
return JS::js_undefined(); \
}
ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE)
#undef __ENUMERATE

View file

@ -80,30 +80,6 @@ public:
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
{
return !m_exception.template has<Empty>();