LibWeb: Do not create a RootVector to invoke IDL callbacks

These callbacks are evaluated synchronously via JS::Call. We do not need
to construct an expensive RootVector container just to immediately
invoke the callbacks.

Stylistically, this also helps indicate where the actual arguments start
at the call sites, by wrapping the arguments in braces.
This commit is contained in:
Timothy Flynn 2025-04-15 20:56:03 -04:00 committed by Tim Flynn
commit 6dd2a4c945
Notes: github-actions[bot] 2025-04-16 11:32:56 +00:00
20 changed files with 60 additions and 119 deletions

View file

@ -713,7 +713,7 @@ void queue_mutation_observer_microtask(DOM::Document const& document)
MUST(wrapped_records->create_data_property(property_index, record.ptr()));
}
(void)WebIDL::invoke_callback(callback, mutation_observer, WebIDL::ExceptionBehavior::Report, wrapped_records, mutation_observer);
(void)WebIDL::invoke_callback(callback, mutation_observer, WebIDL::ExceptionBehavior::Report, { { wrapped_records, mutation_observer } });
}
}

View file

@ -4569,7 +4569,7 @@ void Document::queue_intersection_observer_task()
// 5. Invoke callback with queue as the first argument, observer as the second argument, and observer as the callback this value. If this throws an exception, report the exception.
// NOTE: This does not follow the spec as written precisely, but this is the same thing we do elsewhere and there is a WPT test that relies on this.
(void)WebIDL::invoke_callback(callback, observer.ptr(), WebIDL::ExceptionBehavior::Report, wrapped_queue, observer.ptr());
(void)WebIDL::invoke_callback(callback, observer.ptr(), WebIDL::ExceptionBehavior::Report, { { wrapped_queue, observer.ptr() } });
}
}));
}

View file

@ -2517,7 +2517,7 @@ JS::ThrowCompletionOr<void> Element::upgrade_element(GC::Ref<HTML::CustomElement
set_custom_element_state(CustomElementState::Precustomized);
// 3. Let constructResult be the result of constructing C, with no arguments.
auto construct_result = TRY(WebIDL::construct(constructor));
auto construct_result = TRY(WebIDL::construct(constructor, {}));
// 4. If SameValue(constructResult, element) is false, then throw a TypeError.
if (!JS::same_value(construct_result, this))

View file

@ -578,7 +578,7 @@ WebIDL::ExceptionOr<GC::Ref<Element>> create_element(Document& document, FlyStri
auto& constructor = definition->constructor();
// 2. Set result to the result of constructing C, with no arguments.
auto result = TRY(WebIDL::construct(constructor));
auto result = TRY(WebIDL::construct(constructor, {}));
// NOTE: IDL does not currently convert the object for us, so we will have to do it here.
if (!result.is_object() || !is<HTML::HTMLElement>(result.as_object()))

View file

@ -91,7 +91,7 @@ bool EventDispatcher::inner_invoke(Event& event, Vector<GC::Root<DOM::DOMEventLi
// FIXME: These should be wrapped for us in call_user_object_operation, but it currently doesn't do that.
auto* this_value = event.current_target().ptr();
auto* wrapped_event = &event;
auto result = WebIDL::call_user_object_operation(callback, "handleEvent"_string, this_value, wrapped_event);
auto result = WebIDL::call_user_object_operation(callback, "handleEvent"_string, this_value, { { wrapped_event } });
// If this throws an exception, then:
if (result.is_error()) {

View file

@ -698,7 +698,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// calls directly into the callback without considering things such as proxies, it is a waste. However, if it observable, then we must reuse the this_value that was given to the callback.
auto* this_value = error_event.current_target().ptr();
return_value_or_error = WebIDL::invoke_callback(*callback, this_value, wrapped_message, wrapped_filename, wrapped_lineno, wrapped_colno, error_event.error());
return_value_or_error = WebIDL::invoke_callback(*callback, this_value, { { wrapped_message, wrapped_filename, wrapped_lineno, wrapped_colno, error_event.error() } });
} else {
// -> Otherwise
// Invoke callback with one argument, the value of which is the Event object event, with the callback this value set to event's currentTarget. Let return value be the callback's return value. [WEBIDL]
@ -709,7 +709,7 @@ JS::ThrowCompletionOr<void> EventTarget::process_event_handler_for_event(FlyStri
// FIXME: The comments about this in the special_error_event_handling path also apply here.
auto* this_value = event.current_target().ptr();
return_value_or_error = WebIDL::invoke_callback(*callback, this_value, wrapped_event);
return_value_or_error = WebIDL::invoke_callback(*callback, this_value, { { wrapped_event } });
}
// If an exception gets thrown by the callback, end these steps and allow the exception to propagate. (It will propagate to the DOM event dispatch logic, which will then report the exception.)

View file

@ -169,7 +169,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> NodeIterator::filter(Node& node)
// 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ».
// If this throws an exception, then unset traversers active flag and rethrow the exception.
auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, &node);
auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, { { &node } });
if (result.is_abrupt()) {
m_active = false;
return result;

View file

@ -274,7 +274,7 @@ JS::ThrowCompletionOr<NodeFilter::Result> TreeWalker::filter(Node& node)
// 6. Let result be the return value of call a user objects operation with traversers filter, "acceptNode", and « node ».
// If this throws an exception, then unset traversers active flag and rethrow the exception.
auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, &node);
auto result = WebIDL::call_user_object_operation(m_filter->callback(), "acceptNode"_string, {}, { { &node } });
if (result.is_abrupt()) {
m_active = false;
return result;

View file

@ -117,7 +117,7 @@ void DataTransferItem::get_as_string(GC::Ptr<WebIDL::CallbackType> callback) con
HTML::queue_a_task(HTML::Task::Source::Unspecified, nullptr, nullptr,
GC::Function<void()>::create(realm.heap(), [callback, data]() {
(void)WebIDL::invoke_callback(*callback, {}, data);
(void)WebIDL::invoke_callback(*callback, {}, { { data } });
}));
}

View file

@ -397,7 +397,7 @@ WebIDL::ExceptionOr<void> HTMLCanvasElement::to_blob(GC::Ref<WebIDL::CallbackTyp
blob_result = FileAPI::Blob::create(realm(), file_result->buffer, TRY_OR_THROW_OOM(vm(), String::from_utf8(file_result->mime_type)));
// 2. Invoke callback with « result » and "report".
TRY(WebIDL::invoke_callback(*callback, {}, WebIDL::ExceptionBehavior::Report, move(blob_result)));
TRY(WebIDL::invoke_callback(*callback, {}, WebIDL::ExceptionBehavior::Report, { { blob_result } }));
return {};
});
if (maybe_error.is_throw_completion())

View file

@ -1141,7 +1141,7 @@ bool Navigation::inner_navigate_event_firing_algorithm(
// 2. For each handler of event's navigation handler list:
for (auto const& handler : event->navigation_handler_list()) {
// 1. Append the result of invoking handler with an empty arguments list to promisesList.
auto result = WebIDL::invoke_callback(handler, {});
auto result = WebIDL::invoke_callback(handler, {}, {});
// This *should* be equivalent to converting a promise to a promise capability
promises_list.append(WebIDL::create_resolved_promise(realm, result.value()));
}

View file

@ -86,7 +86,7 @@ void UniversalGlobalScopeMixin::queue_microtask(WebIDL::CallbackType& callback)
// The queueMicrotask(callback) method must queue a microtask to invoke callback with « » and "report".
HTML::queue_a_microtask(document, GC::create_function(realm.heap(), [&callback] {
(void)WebIDL::invoke_callback(callback, {}, WebIDL::ExceptionBehavior::Report);
(void)WebIDL::invoke_callback(callback, {}, WebIDL::ExceptionBehavior::Report, {});
}));
}

View file

@ -1611,7 +1611,7 @@ WebIDL::UnsignedLong Window::request_animation_frame(GC::Ref<WebIDL::CallbackTyp
// FIXME: Make this fully spec compliant. Currently implements a mix of 'requestAnimationFrame()' and 'run the animation frame callbacks'.
return animation_frame_callback_driver().add(GC::create_function(heap(), [this, callback](double now) {
// 3. Invoke callback, passing now as the only argument, and if an exception is thrown, report the exception.
auto result = WebIDL::invoke_callback(*callback, {}, JS::Value(now));
auto result = WebIDL::invoke_callback(*callback, {}, { { JS::Value(now) } });
if (result.is_error())
report_exception(result, realm());
}));
@ -1655,7 +1655,7 @@ u32 Window::request_idle_callback(WebIDL::CallbackType& callback, RequestIdleCal
// 4. Push callback to the end of window's list of idle request callbacks, associated with handle.
auto handler = [callback = GC::make_root(callback)](GC::Ref<RequestIdleCallback::IdleDeadline> deadline) -> JS::Completion {
return WebIDL::invoke_callback(*callback, {}, deadline.ptr());
return WebIDL::invoke_callback(*callback, {}, { { deadline } });
};
m_idle_request_callbacks.append(adopt_ref(*new IdleCallback(move(handler), handle)));

View file

@ -639,7 +639,7 @@ void WindowOrWorkerGlobalScopeMixin::queue_the_performance_observer_task()
// 9. Call pos observer callback with observerEntryList as the first argument, with po as the second
// argument and as callback this value, and with callbackOptions as the third argument.
// If this throws an exception, report the exception.
auto completion = WebIDL::invoke_callback(registered_observer->callback(), registered_observer, observer_entry_list, registered_observer, callback_options);
auto completion = WebIDL::invoke_callback(registered_observer->callback(), registered_observer, { { observer_entry_list, registered_observer, callback_options } });
if (completion.is_abrupt())
HTML::report_exception(completion, realm);
}

View file

@ -109,7 +109,7 @@ void ResizeObserver::invoke_callback(ReadonlySpan<GC::Ref<ResizeObserverEntry>>
MUST(wrapped_records->create_data_property(property_index, record.ptr()));
}
(void)WebIDL::invoke_callback(callback, JS::js_undefined(), WebIDL::ExceptionBehavior::Report, wrapped_records);
(void)WebIDL::invoke_callback(callback, JS::js_undefined(), WebIDL::ExceptionBehavior::Report, { { wrapped_records } });
}
}

View file

@ -1762,7 +1762,8 @@ GC::Ref<SizeAlgorithm> extract_size_algorithm(JS::VM& vm, QueuingStrategy const&
// 2. Return an algorithm that performs the following steps, taking a chunk argument:
return GC::create_function(vm.heap(), [size = strategy.size](JS::Value chunk) {
return WebIDL::invoke_callback(*size, JS::js_undefined(), chunk);
// 1. Return the result of invoking strategy["size"] with argument list « chunk ».
return WebIDL::invoke_callback(*size, {}, { { chunk } });
});
}
@ -3145,7 +3146,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
// invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source.start) {
start_algorithm = GC::create_function(realm.heap(), [controller, underlying_source_value, callback = underlying_source.start]() -> WebIDL::ExceptionOr<JS::Value> {
return TRY(WebIDL::invoke_callback(*callback, underlying_source_value, controller));
return TRY(WebIDL::invoke_callback(*callback, underlying_source_value, { { controller } }));
});
}
@ -3153,7 +3154,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
// invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source.pull) {
pull_algorithm = GC::create_function(realm.heap(), [controller, underlying_source_value, callback = underlying_source.pull]() {
return WebIDL::invoke_promise_callback(*callback, underlying_source_value, controller);
return WebIDL::invoke_promise_callback(*callback, underlying_source_value, { { controller } });
});
}
@ -3162,7 +3163,7 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
// callback this value underlyingSource.
if (underlying_source.cancel) {
cancel_algorithm = GC::create_function(realm.heap(), [underlying_source_value, callback = underlying_source.cancel](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, underlying_source_value, reason);
return WebIDL::invoke_promise_callback(*callback, underlying_source_value, { { reason } });
});
}
@ -4846,7 +4847,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// callback this value underlyingSink.
if (underlying_sink.start) {
start_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.start]() -> WebIDL::ExceptionOr<JS::Value> {
return TRY(WebIDL::invoke_callback(*callback, underlying_sink_value, WebIDL::ExceptionBehavior::Rethrow, controller));
return TRY(WebIDL::invoke_callback(*callback, underlying_sink_value, WebIDL::ExceptionBehavior::Rethrow, { { controller } }));
});
}
@ -4855,7 +4856,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// callback this value underlyingSink.
if (underlying_sink.write) {
write_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.write](JS::Value chunk) {
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, chunk, controller);
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, { { chunk, controller } });
});
}
@ -4863,7 +4864,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// invoking underlyingSinkDict["close"] with argument list «» and callback this value underlyingSink.
if (underlying_sink.close) {
close_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.close]() {
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value);
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, {});
});
}
@ -4872,7 +4873,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underly
// value underlyingSink.
if (underlying_sink.abort) {
abort_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.abort](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, reason);
return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, { { reason } });
});
}
@ -5275,7 +5276,7 @@ void set_up_transform_stream_default_controller_from_transformer(TransformStream
// callback this value transformer.
if (transformer_dict.transform) {
transform_algorithm = GC::create_function(realm.heap(), [controller, transformer, callback = transformer_dict.transform](JS::Value chunk) {
return WebIDL::invoke_promise_callback(*callback, transformer, chunk, controller);
return WebIDL::invoke_promise_callback(*callback, transformer, { { chunk, controller } });
});
}
@ -5283,7 +5284,7 @@ void set_up_transform_stream_default_controller_from_transformer(TransformStream
// transformerDict["flush"] with argument list « controller » and callback this value transformer.
if (transformer_dict.flush) {
flush_algorithm = GC::create_function(realm.heap(), [transformer, callback = transformer_dict.flush, controller]() {
return WebIDL::invoke_promise_callback(*callback, transformer, controller);
return WebIDL::invoke_promise_callback(*callback, transformer, { { controller } });
});
}
@ -5291,7 +5292,7 @@ void set_up_transform_stream_default_controller_from_transformer(TransformStream
// the result of invoking transformerDict["cancel"] with argument list « reason » and callback this value transformer.
if (transformer_dict.cancel) {
cancel_algorithm = GC::create_function(realm.heap(), [transformer, callback = transformer_dict.cancel](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, transformer, reason);
return WebIDL::invoke_promise_callback(*callback, transformer, { { reason } });
});
}
@ -5816,7 +5817,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
// invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source_dict.start) {
start_algorithm = GC::create_function(realm.heap(), [controller, underlying_source, callback = underlying_source_dict.start]() -> WebIDL::ExceptionOr<JS::Value> {
return TRY(WebIDL::invoke_callback(*callback, underlying_source, controller));
return TRY(WebIDL::invoke_callback(*callback, underlying_source, { { controller } }));
});
}
@ -5824,7 +5825,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
// invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource.
if (underlying_source_dict.pull) {
pull_algorithm = GC::create_function(realm.heap(), [controller, underlying_source, callback = underlying_source_dict.pull]() {
return WebIDL::invoke_promise_callback(*callback, underlying_source, controller);
return WebIDL::invoke_promise_callback(*callback, underlying_source, { { controller } });
});
}
@ -5833,7 +5834,7 @@ WebIDL::ExceptionOr<void> set_up_readable_byte_stream_controller_from_underlying
// callback this value underlyingSource.
if (underlying_source_dict.cancel) {
cancel_algorithm = GC::create_function(realm.heap(), [underlying_source, callback = underlying_source_dict.cancel](JS::Value reason) {
return WebIDL::invoke_promise_callback(*callback, underlying_source, reason);
return WebIDL::invoke_promise_callback(*callback, underlying_source, { { reason } });
});
}

View file

@ -63,7 +63,7 @@ WebIDL::ExceptionOr<GC::Ref<TransformStream>> TransformStream::construct_impl(JS
// 12. If transformerDict["start"] exists, then resolve startPromise with the result of invoking
// transformerDict["start"] with argument list « this.[[controller]] » and callback this value transformer.
if (transformer_dict.start) {
auto result = TRY(WebIDL::invoke_callback(*transformer_dict.start, transformer, stream->controller()));
auto result = TRY(WebIDL::invoke_callback(*transformer_dict.start, transformer, { { stream->controller() } }));
WebIDL::resolve_promise(realm, start_promise, result);
}
// 13. Otherwise, resolve startPromise with undefined.

View file

@ -243,7 +243,7 @@ GC::Ref<WebIDL::Promise> BaseAudioContext::decode_audio_data(GC::Root<WebIDL::Bu
// 4.3. Queue a media element task to invoke errorCallback with error.
if (error_callback) {
queue_a_media_element_task(GC::create_function(heap(), [&realm, error_callback, error] {
auto completion = WebIDL::invoke_callback(*error_callback, {}, error);
auto completion = WebIDL::invoke_callback(*error_callback, {}, { { error } });
if (completion.is_abrupt())
HTML::report_exception(completion, realm);
}));
@ -293,7 +293,7 @@ void BaseAudioContext::queue_a_decoding_operation(GC::Ref<JS::PromiseCapability>
// 4.2. If errorCallback is not missing, invoke errorCallback with error.
if (error_callback) {
auto completion = WebIDL::invoke_callback(*error_callback, {}, error);
auto completion = WebIDL::invoke_callback(*error_callback, {}, { { error } });
if (completion.is_abrupt())
HTML::report_exception(completion, realm);
}
@ -317,7 +317,7 @@ void BaseAudioContext::queue_a_decoding_operation(GC::Ref<JS::PromiseCapability>
// 5.2.3. If successCallback is not missing, invoke successCallback with buffer.
if (success_callback) {
auto completion = WebIDL::invoke_callback(*success_callback, {}, buffer);
auto completion = WebIDL::invoke_callback(*success_callback, {}, { { buffer } });
if (completion.is_abrupt())
HTML::report_exception(completion, realm);
}

View file

@ -8,17 +8,17 @@
#include <AK/ByteBuffer.h>
#include <AK/Enumerate.h>
#include <AK/Math.h>
#include <AK/NumericLimits.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DataView.h>
#include <LibJS/Runtime/PropertyKey.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/ValueInlines.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
#include <LibWeb/WebIDL/CallbackType.h>
#include <LibWeb/WebIDL/Promise.h>
#include <LibWeb/WebIDL/Types.h>
@ -141,7 +141,7 @@ inline JS::Completion clean_up_on_return(JS::Realm& stored_realm, JS::Realm& rel
// https://webidl.spec.whatwg.org/#call-a-user-objects-operation
// https://whatpr.org/webidl/1437.html#call-a-user-objects-operation
JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args)
JS::Completion call_user_object_operation(CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args)
{
// 1. Let completion be an uninitialized variable.
JS::Completion completion;
@ -197,9 +197,7 @@ JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String
// For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to.
// 11. Let callResult be Call(X, thisArg, esArgs).
VERIFY(actual_function_object);
auto& vm = object->vm();
auto call_result = JS::call(vm, as<JS::FunctionObject>(*actual_function_object), this_argument.value(), args.span());
auto call_result = JS::call(object->vm(), as<JS::FunctionObject>(*actual_function_object), this_argument.value(), args);
// 12. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
if (call_result.is_throw_completion()) {
@ -244,7 +242,7 @@ JS::ThrowCompletionOr<String> to_usv_string(JS::VM& vm, JS::Value value)
// https://webidl.spec.whatwg.org/#invoke-a-callback-function
// https://whatpr.org/webidl/1437.html#invoke-a-callback-function
template<typename ReturnSteps>
static auto invoke_callback_impl(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args, ReturnSteps&& return_steps)
static auto invoke_callback_impl(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args, ReturnSteps&& return_steps)
{
// 1. Let completion be an uninitialized variable.
@ -279,8 +277,7 @@ static auto invoke_callback_impl(WebIDL::CallbackType& callback, Optional<JS::Va
// If this throws an exception, set completion to the completion value representing the thrown exception and jump to the step labeled return.
// 10. Let callResult be Call(F, thisArg, jsArgs).
auto& vm = function_object->vm();
auto call_result = JS::call(vm, as<JS::FunctionObject>(*function_object), this_argument.value(), args.span());
auto call_result = JS::call(function_object->vm(), as<JS::FunctionObject>(*function_object), this_argument.value(), args);
// 11. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
// 12. Set completion to the result of converting callResult.[[Value]] to an IDL value of the same type as callables
@ -298,7 +295,7 @@ static auto invoke_callback_impl(WebIDL::CallbackType& callback, Optional<JS::Va
}
}
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, GC::RootVector<JS::Value> args)
JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, ReadonlySpan<JS::Value> args)
{
// https://webidl.spec.whatwg.org/#js-invoking-callback-functions
// The exceptionBehavior argument must be supplied if, and only if, callables return type is not a promise type. If callables return type is neither undefined nor any, it must be "rethrow".
@ -308,7 +305,7 @@ JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Valu
VERIFY(exception_behavior == ExceptionBehavior::NotSpecified || callback.operation_returns_promise == OperationReturnsPromise::No);
return invoke_callback_impl(callback, move(this_argument), move(args), [&](JS::Realm& relevant_realm, JS::Completion completion) -> JS::Completion {
return invoke_callback_impl(callback, move(this_argument), args, [&](JS::Realm& relevant_realm, JS::Completion completion) -> JS::Completion {
// 3. If completion is an IDL value, return completion.
if (!completion.is_abrupt())
return completion;
@ -344,21 +341,21 @@ JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Valu
});
}
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args)
JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args)
{
return invoke_callback(callback, move(this_argument), ExceptionBehavior::NotSpecified, move(args));
return invoke_callback(callback, move(this_argument), ExceptionBehavior::NotSpecified, args);
}
// AD-HOC: This may be used as an alternative to WebIDL::invoke_callback when you know the callback returns a promise,
// and the caller needs a WebIDL::Promise rather than a JS::Promise.
GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args)
GC::Ref<Promise> invoke_promise_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args)
{
VERIFY(callback.operation_returns_promise == OperationReturnsPromise::Yes);
return invoke_callback_impl(callback, move(this_argument), move(args), [&](JS::Realm& relevant_realm, JS::Completion completion) -> GC::Ref<WebIDL::Promise> {
return invoke_callback_impl(callback, move(this_argument), args, [&](JS::Realm& relevant_realm, JS::Completion completion) {
// 3. If completion is an IDL value, return completion.
if (!completion.is_abrupt())
return WebIDL::create_resolved_promise(relevant_realm, completion.release_value());
return create_resolved_promise(relevant_realm, completion.release_value());
// 4. Assert: completion is an abrupt completion.
VERIFY(completion.is_abrupt());
@ -371,7 +368,7 @@ GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback,
});
}
JS::Completion construct(WebIDL::CallbackType& callback, GC::RootVector<JS::Value> args)
JS::Completion construct(CallbackType& callback, ReadonlySpan<JS::Value> args)
{
// 1. Let completion be an uninitialized variable.
JS::Completion completion;
@ -399,8 +396,7 @@ JS::Completion construct(WebIDL::CallbackType& callback, GC::RootVector<JS::Valu
// For simplicity, we currently make the caller do this. However, this means we can't throw exceptions at this point like the spec wants us to.
// 8. Let callResult be Completion(Construct(F, esArgs)).
auto& vm = function_object->vm();
auto call_result = JS::construct(vm, as<JS::FunctionObject>(*function_object), args.span());
auto call_result = JS::construct(function_object->vm(), as<JS::FunctionObject>(*function_object), args);
// 9. If callResult is an abrupt completion, set completion to callResult and jump to the step labeled return.
if (call_result.is_throw_completion()) {

View file

@ -9,11 +9,9 @@
#pragma once
#include <AK/Forward.h>
#include <LibGC/Ptr.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/WebIDL/CallbackType.h>
#include <LibWeb/Forward.h>
namespace Web::WebIDL {
@ -21,77 +19,23 @@ bool is_buffer_source_type(JS::Value);
GC::Ptr<JS::ArrayBuffer> underlying_buffer_source(JS::Object& buffer_source);
ErrorOr<ByteBuffer> get_buffer_source_copy(JS::Object const& buffer_source);
JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args);
JS::Completion call_user_object_operation(CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args);
JS::ThrowCompletionOr<String> to_string(JS::VM&, JS::Value);
JS::ThrowCompletionOr<String> to_usv_string(JS::VM&, JS::Value);
JS::ThrowCompletionOr<String> to_byte_string(JS::VM&, JS::Value);
// https://webidl.spec.whatwg.org/#call-a-user-objects-operation
template<typename... Args>
JS::Completion call_user_object_operation(WebIDL::CallbackType& callback, String const& operation_name, Optional<JS::Value> this_argument, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return call_user_object_operation(callback, operation_name, move(this_argument), move(arguments_list));
}
enum class ExceptionBehavior {
NotSpecified,
Report,
Rethrow,
};
JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, ReadonlySpan<JS::Value> args);
JS::Completion invoke_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args);
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, GC::RootVector<JS::Value> args);
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args);
GC::Ref<Promise> invoke_promise_callback(CallbackType& callback, Optional<JS::Value> this_argument, ReadonlySpan<JS::Value> args);
// https://webidl.spec.whatwg.org/#invoke-a-callback-function
template<typename... Args>
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, ExceptionBehavior exception_behavior, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return invoke_callback(callback, move(this_argument), exception_behavior, move(arguments_list));
}
template<typename... Args>
JS::Completion invoke_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, Args&&... args)
{
return invoke_callback(callback, move(this_argument), ExceptionBehavior::NotSpecified, forward<Args>(args)...);
}
GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, GC::RootVector<JS::Value> args);
template<typename... Args>
GC::Ref<WebIDL::Promise> invoke_promise_callback(WebIDL::CallbackType& callback, Optional<JS::Value> this_argument, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return invoke_promise_callback(callback, move(this_argument), move(arguments_list));
}
JS::Completion construct(WebIDL::CallbackType& callback, GC::RootVector<JS::Value> args);
// https://webidl.spec.whatwg.org/#construct-a-callback-function
template<typename... Args>
JS::Completion construct(WebIDL::CallbackType& callback, Args&&... args)
{
auto& function_object = callback.callback;
GC::RootVector<JS::Value> arguments_list { function_object->heap() };
(arguments_list.append(forward<Args>(args)), ...);
return construct(callback, move(arguments_list));
}
JS::Completion construct(CallbackType& callback, ReadonlySpan<JS::Value> args);
// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
double integer_part(double n);