Selaa lähdekoodia

LibWeb: Add support for converting IDL dictionaries to native structs

Idan Horowitz 3 vuotta sitten
vanhempi
commit
071a358ffb
1 muutettua tiedostoa jossa 59 lisäystä ja 11 poistoa
  1. 59 11
      Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp

+ 59 - 11
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp

@@ -818,7 +818,7 @@ static bool is_wrappable_type(IDL::Type const& type)
 }
 
 template<typename ParameterType>
-static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter, String const& js_name, String const& js_suffix, String const& cpp_name, bool return_void = false, bool legacy_null_to_empty_string = false, bool optional = false, Optional<String> optional_default_value = {})
+static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter, String const& js_name, String const& js_suffix, String const& cpp_name, HashMap<String, IDL::Dictionary> const& dictionaries, bool return_void = false, bool legacy_null_to_empty_string = false, bool optional = false, Optional<String> optional_default_value = {})
 {
     auto scoped_generator = generator.fork();
     scoped_generator.set("cpp_name", make_input_acceptable_cpp(cpp_name));
@@ -1038,6 +1038,54 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
                 }
             }
         }
+    } else if (dictionaries.contains(parameter.type.name)) {
+        if (optional_default_value.has_value() && optional_default_value != "{}")
+            TODO();
+        auto dictionary_generator = scoped_generator.fork();
+        dictionary_generator.append(R"~~~(
+    if (!@js_name@@js_suffix@.is_nullish() && !@js_name@@js_suffix@.is_object()) {
+        vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotAnObjectOfType, "@parameter.type.name@");
+        @return_statement@
+    }
+    @parameter.type.name@ @cpp_name@ {};
+)~~~");
+        auto* current_dictionary = &dictionaries.find(parameter.type.name)->value;
+        while (true) {
+            for (auto& member : current_dictionary->members) {
+                dictionary_generator.set("member_key", member.name);
+                auto member_js_name = make_input_acceptable_cpp(member.name.to_snakecase());
+                dictionary_generator.set("member_name", member_js_name);
+                dictionary_generator.append(R"~~~(
+    JS::Value @member_name@;
+    if (@js_name@@js_suffix@.is_nullish()) {
+        @member_name@ = JS::js_undefined();
+    } else {
+        @member_name@ = @js_name@@js_suffix@.as_object().get("@member_key@");
+        if (vm.exception())
+            @return_statement@
+    }
+)~~~");
+                if (member.required) {
+                    dictionary_generator.append(R"~~~(
+    if (@member_name@.is_undefined()) {
+        vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::MissingRequiredProperty, "@member_key@");
+        @return_statement@
+    }
+)~~~");
+                }
+
+                auto member_value_name = String::formatted("{}_value", member_js_name);
+                dictionary_generator.set("member_value_name", member_value_name);
+                generate_to_cpp(dictionary_generator, member, member_js_name, "", member_value_name, dictionaries, return_void, member.extended_attributes.contains("LegacyNullToEmptyString"), !member.required, member.default_value);
+                dictionary_generator.append(R"~~~(
+    @cpp_name@.@member_name@ = @member_value_name@;
+)~~~");
+            }
+            if (current_dictionary->parent_name.is_null())
+                break;
+            VERIFY(dictionaries.contains(current_dictionary->parent_name));
+            current_dictionary = &dictionaries.find(current_dictionary->parent_name)->value;
+        }
     } else {
         dbgln("Unimplemented JS-to-C++ conversion: {}", parameter.type.name);
         VERIFY_NOT_REACHED();
@@ -1069,7 +1117,7 @@ static void generate_argument_count_check(SourceGenerator& generator, FunctionTy
 )~~~");
 }
 
-static void generate_arguments(SourceGenerator& generator, Vector<IDL::Parameter> const& parameters, StringBuilder& arguments_builder, bool return_void = false)
+static void generate_arguments(SourceGenerator& generator, Vector<IDL::Parameter> const& parameters, StringBuilder& arguments_builder, HashMap<String, IDL::Dictionary> const& dictionaries, bool return_void = false)
 {
     auto arguments_generator = generator.fork();
 
@@ -1083,7 +1131,7 @@ static void generate_arguments(SourceGenerator& generator, Vector<IDL::Parameter
     auto arg@argument.index@ = vm.argument(@argument.index@);
 )~~~");
         bool legacy_null_to_empty_string = parameter.extended_attributes.contains("LegacyNullToEmptyString");
-        generate_to_cpp(generator, parameter, "arg", String::number(argument_index), parameter.name.to_snakecase(), return_void, legacy_null_to_empty_string, parameter.optional, parameter.optional_default_value);
+        generate_to_cpp(generator, parameter, "arg", String::number(argument_index), parameter.name.to_snakecase(), dictionaries, return_void, legacy_null_to_empty_string, parameter.optional, parameter.optional_default_value);
         ++argument_index;
     }
 
@@ -1191,7 +1239,7 @@ static void generate_variable_statement(SourceGenerator& generator, String const
     return generate_wrap_statement(generator, value_name, value_type, String::formatted("{} = ", variable_name));
 }
 
-static void generate_function(SourceGenerator& generator, IDL::Function const& function, StaticFunction is_static_function, String const& class_name, String const& interface_fully_qualified_name)
+static void generate_function(SourceGenerator& generator, IDL::Function const& function, StaticFunction is_static_function, String const& class_name, String const& interface_fully_qualified_name, HashMap<String, IDL::Dictionary> const& dictionaries)
 {
     auto function_generator = generator.fork();
     function_generator.set("class_name", class_name);
@@ -1222,7 +1270,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
     generate_argument_count_check(generator, function);
 
     StringBuilder arguments_builder;
-    generate_arguments(generator, function.parameters, arguments_builder);
+    generate_arguments(generator, function.parameters, arguments_builder, dictionaries);
     function_generator.set(".arguments", arguments_builder.string_view());
 
     if (is_static_function == StaticFunction::No) {
@@ -1802,7 +1850,7 @@ static void invoke_named_property_setter(JS::GlobalObject& global_object, @fully
 
             // 4. Let value be the result of converting V to an IDL value of type T.
             // NOTE: This takes the last parameter as it's enforced that there's only two parameters.
-            generate_to_cpp(scoped_generator, interface.named_property_setter->parameters.last(), "value", "", "converted_value", true);
+            generate_to_cpp(scoped_generator, interface.named_property_setter->parameters.last(), "value", "", "converted_value", interface.dictionaries, true);
 
             // 5. If operation was defined without an identifier, then:
             if (interface.named_property_setter->name.is_empty()) {
@@ -1860,7 +1908,7 @@ static void invoke_indexed_property_setter(JS::GlobalObject& global_object, @ful
 
             // 5. Let value be the result of converting V to an IDL value of type T.
             // NOTE: This takes the last parameter as it's enforced that there's only two parameters.
-            generate_to_cpp(scoped_generator, interface.named_property_setter->parameters.last(), "value", "", "converted_value", true);
+            generate_to_cpp(scoped_generator, interface.named_property_setter->parameters.last(), "value", "", "converted_value", interface.dictionaries, true);
 
             // 6. If operation was defined without an identifier, then:
             if (interface.indexed_property_setter->name.is_empty()) {
@@ -2425,7 +2473,7 @@ JS::Value @constructor_class@::construct(FunctionObject&)
             generate_argument_count_check(generator, constructor);
 
             StringBuilder arguments_builder;
-            generate_arguments(generator, constructor.parameters, arguments_builder);
+            generate_arguments(generator, constructor.parameters, arguments_builder, interface.dictionaries);
             generator.set(".constructor_arguments", arguments_builder.string_view());
 
             generator.append(R"~~~(
@@ -2489,7 +2537,7 @@ define_direct_property("@constant.name@", JS::Value((i32)@constant.value@), JS::
 
     // Implementation: Static Functions
     for (auto& function : interface.static_functions) {
-        generate_function(generator, function, StaticFunction::Yes, interface.constructor_class, interface.fully_qualified_name);
+        generate_function(generator, function, StaticFunction::Yes, interface.constructor_class, interface.fully_qualified_name, interface.dictionaries);
     }
 
     generator.append(R"~~~(
@@ -2914,7 +2962,7 @@ JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@attribute.setter_callback@)
     auto value = vm.argument(0);
 )~~~");
 
-            generate_to_cpp(generator, attribute, "value", "", "cpp_value", false, attribute.extended_attributes.contains("LegacyNullToEmptyString"));
+            generate_to_cpp(generator, attribute, "value", "", "cpp_value", interface.dictionaries, false, attribute.extended_attributes.contains("LegacyNullToEmptyString"));
 
             if (attribute.extended_attributes.contains("Reflect")) {
                 if (attribute.type.name != "boolean") {
@@ -2947,7 +2995,7 @@ JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@attribute.setter_callback@)
 
     // Implementation: Functions
     for (auto& function : interface.functions) {
-        generate_function(generator, function, StaticFunction::No, interface.prototype_class, interface.fully_qualified_name);
+        generate_function(generator, function, StaticFunction::No, interface.prototype_class, interface.fully_qualified_name, interface.dictionaries);
     }
 
     if (interface.has_stringifier) {