LibWeb: Add support for converting IDL dictionaries to native structs

This commit is contained in:
Idan Horowitz 2021-09-29 01:32:29 +03:00
parent 5ce468338e
commit 071a358ffb

View file

@ -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) {