diff options
author | Idan Horowitz <idan.horowitz@gmail.com> | 2022-03-30 23:35:13 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-31 01:10:47 +0200 |
commit | 1c4f128fd126ea70c557114fe80d54ee4a7f8757 (patch) | |
tree | cb4635b10070062feb2707a3cd0168ab0850b9c5 /Meta | |
parent | 9ff79c9d54dba1591968df6e39f57c8e8dc73fce (diff) | |
download | serenity-1c4f128fd126ea70c557114fe80d54ee4a7f8757.zip |
LibWeb: Add support for IDL callback functions
Diffstat (limited to 'Meta')
4 files changed, 84 insertions, 0 deletions
diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLGenerators.cpp index 1cf8c31387..0b6cdd61d4 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLGenerators.cpp @@ -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"~~~( diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.cpp index 08a391dfaf..8c1fe69851 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.cpp @@ -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 diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.h b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.h index 4ce432772c..db8148bcc3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.h +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.h @@ -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&); diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLTypes.h b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLTypes.h index 39faff027f..391891d335 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLTypes.h +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLTypes.h @@ -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; |