LibWeb: Add support for IDL callback functions

This commit is contained in:
Idan Horowitz 2022-03-30 23:35:13 +03:00 committed by Andreas Kling
parent 9ff79c9d54
commit 1c4f128fd1
Notes: sideshowbarker 2024-07-17 16:26:50 +09:00
4 changed files with 84 additions and 0 deletions

View File

@ -640,6 +640,32 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
VERIFY(interface.dictionaries.contains(current_dictionary->parent_name));
current_dictionary = &interface.dictionaries.find(current_dictionary->parent_name)->value;
}
} else if (interface.callback_functions.contains(parameter.type->name)) {
// https://webidl.spec.whatwg.org/#es-callback-function
auto callback_function_generator = scoped_generator.fork();
auto& callback_function = interface.callback_functions.find(parameter.type->name)->value;
// An ECMAScript value V is converted to an IDL callback function type value by running the following algorithm:
// 1. If the result of calling IsCallable(V) is false and the conversion to an IDL value is not being performed due to V being assigned to an attribute whose type is a nullable callback function that is annotated with [LegacyTreatNonObjectAsNull], then throw a TypeError.
if (!callback_function.is_legacy_treat_non_object_as_null) {
callback_function_generator.append(R"~~~(
if (!@js_name@@js_suffix@.is_function())
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAFunction, @js_name@@js_suffix@.to_string_without_side_effects());
)~~~");
}
// 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent settings object as the callback context.
if (callback_function.is_legacy_treat_non_object_as_null) {
callback_function_generator.append(R"~~~(
Optional<Bindings::CallbackType> @cpp_name@;
if (@js_name@@js_suffix@.is_object())
@cpp_name@ = Bindings::CallbackType { JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object() };
)~~~");
} else {
callback_function_generator.append(R"~~~(
auto @cpp_name@ = Bindings::CallbackType { JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object() };
)~~~");
}
} else if (parameter.type->name == "sequence") {
// https://webidl.spec.whatwg.org/#es-sequence
@ -1342,6 +1368,28 @@ static void generate_wrap_statement(SourceGenerator& generator, String const& va
scoped_generator.append(R"~~~(
@result_expression@ JS::js_string(global_object.heap(), Bindings::idl_enum_to_string(@value@));
)~~~");
} else if (interface.callback_functions.contains(type.name)) {
// https://webidl.spec.whatwg.org/#es-callback-function
auto& callback_function = interface.callback_functions.find(type.name)->value;
// The result of converting an IDL callback function type value to an ECMAScript value is a reference to the same object that the IDL callback function type value represents.
if (callback_function.is_legacy_treat_non_object_as_null && !type.nullable) {
scoped_generator.append(R"~~~(
if (!@value@) {
@result_expression@ JS::js_null();
} else {
VERIFY(!@value@->callback.is_null());
@result_expression@ @value@->callback.cell();
}
)~~~");
} else {
scoped_generator.append(R"~~~(
VERIFY(!@value@->callback.is_null());
@result_expression@ @value@->callback.cell();
)~~~");
}
} else {
if (wrapping_reference == WrappingReference::No) {
scoped_generator.append(R"~~~(

View File

@ -722,6 +722,29 @@ void Parser::parse_interface_mixin(Interface& interface)
interface.mixins.set(move(name), move(mixin_interface));
}
void Parser::parse_callback_function(HashMap<String, String>& extended_attributes, Interface& interface)
{
assert_string("callback");
consume_whitespace();
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
consume_whitespace();
assert_specific('=');
consume_whitespace();
auto return_type = parse_type();
consume_whitespace();
assert_specific('(');
auto parameters = parse_parameters();
assert_specific(')');
consume_whitespace();
assert_specific(';');
interface.callback_functions.set(name, CallbackFunction { move(return_type), move(parameters), extended_attributes.contains("LegacyTreatNonObjectAsNull") });
consume_whitespace();
}
void Parser::parse_non_interface_entities(bool allow_interface, Interface& interface)
{
while (!lexer.is_eof()) {
@ -736,6 +759,8 @@ void Parser::parse_non_interface_entities(bool allow_interface, Interface& inter
parse_typedef(interface);
} else if (lexer.next_is("interface mixin")) {
parse_interface_mixin(interface);
} else if (lexer.next_is("callback")) {
parse_callback_function(extended_attributes, interface);
} else if ((allow_interface && !lexer.next_is("interface")) || !allow_interface) {
auto current_offset = lexer.tell();
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
@ -841,6 +866,9 @@ NonnullOwnPtr<Interface> Parser::parse()
report_parsing_error(String::formatted("Mixin '{}' was already defined in {}", mixin.key, mixin.value->module_own_path), filename, input, lexer.tell());
interface->mixins.set(mixin.key, move(mixin.value));
}
for (auto& callback_function : import.callback_functions)
interface->callback_functions.set(callback_function.key, move(callback_function.value));
}
// Resolve mixins

View File

@ -41,6 +41,7 @@ private:
void parse_typedef(Interface&);
void parse_interface_mixin(Interface&);
void parse_dictionary(Interface&);
void parse_callback_function(HashMap<String, String>& extended_attributes, Interface&);
void parse_constructor(Interface&);
void parse_getter(HashMap<String, String>& extended_attributes, Interface&);
void parse_setter(HashMap<String, String>& extended_attributes, Interface&);

View File

@ -133,6 +133,12 @@ struct Enumeration {
bool is_original_definition { true };
};
struct CallbackFunction {
NonnullRefPtr<Type> return_type;
Vector<Parameter> parameters;
bool is_legacy_treat_non_object_as_null { false };
};
struct Interface;
struct ParameterizedType : public Type {
@ -191,6 +197,7 @@ struct Interface {
HashMap<String, Enumeration> enumerations;
HashMap<String, Typedef> typedefs;
HashMap<String, NonnullOwnPtr<Interface>> mixins;
HashMap<String, CallbackFunction> callback_functions;
// Added for convenience after parsing
String wrapper_class;