IDLGenerators: Support generating dictionary to value converter helpers

This is useful when returning dictionaries as a Promise.
This commit is contained in:
Idan Horowitz 2025-08-05 20:24:27 +03:00 committed by Tim Flynn
commit dc1b7b1925
Notes: github-actions[bot] 2025-08-08 17:12:32 +00:00
4 changed files with 45 additions and 9 deletions

View file

@ -919,7 +919,7 @@ void Parser::parse_typedef(Interface& interface)
consume_whitespace(); consume_whitespace();
} }
void Parser::parse_dictionary(Interface& interface) void Parser::parse_dictionary(HashMap<ByteString, ByteString> extended_attributes, Interface& interface)
{ {
bool partial = false; bool partial = false;
if (lexer.next_is("partial")) { if (lexer.next_is("partial")) {
@ -932,6 +932,7 @@ void Parser::parse_dictionary(Interface& interface)
consume_whitespace(); consume_whitespace();
Dictionary dictionary {}; Dictionary dictionary {};
dictionary.extended_attributes = move(extended_attributes);
auto name = parse_identifier_ending_with_space(); auto name = parse_identifier_ending_with_space();
consume_whitespace(); consume_whitespace();
@ -953,10 +954,10 @@ void Parser::parse_dictionary(Interface& interface)
} }
bool required = false; bool required = false;
HashMap<ByteString, ByteString> extended_attributes; HashMap<ByteString, ByteString> member_extended_attributes;
if (lexer.consume_specific('[')) if (lexer.consume_specific('['))
extended_attributes = parse_extended_attributes(); member_extended_attributes = parse_extended_attributes();
if (lexer.consume_specific("required"sv)) { if (lexer.consume_specific("required"sv)) {
required = true; required = true;
@ -964,7 +965,7 @@ void Parser::parse_dictionary(Interface& interface)
} }
if (lexer.consume_specific('[')) if (lexer.consume_specific('['))
extended_attributes.update(parse_extended_attributes()); member_extended_attributes.update(parse_extended_attributes());
auto type = parse_type(); auto type = parse_type();
consume_whitespace(); consume_whitespace();
@ -987,7 +988,7 @@ void Parser::parse_dictionary(Interface& interface)
required, required,
move(type), move(type),
move(name), move(name),
move(extended_attributes), move(member_extended_attributes),
Optional<ByteString>(move(default_value)), Optional<ByteString>(move(default_value)),
}; };
dictionary.members.append(move(member)); dictionary.members.append(move(member));
@ -1061,7 +1062,7 @@ void Parser::parse_non_interface_entities(bool allow_interface, Interface& inter
if (lexer.consume_specific('[')) if (lexer.consume_specific('['))
extended_attributes = parse_extended_attributes(); extended_attributes = parse_extended_attributes();
if (lexer.next_is("dictionary") || lexer.next_is("partial dictionary")) { if (lexer.next_is("dictionary") || lexer.next_is("partial dictionary")) {
parse_dictionary(interface); parse_dictionary(extended_attributes, interface);
} else if (lexer.next_is("enum")) { } else if (lexer.next_is("enum")) {
parse_enumeration(extended_attributes, interface); parse_enumeration(extended_attributes, interface);
} else if (lexer.next_is("typedef")) { } else if (lexer.next_is("typedef")) {
@ -1205,7 +1206,11 @@ Interface& Parser::parse()
interface.extend_with_partial_interface(*partial_interface); interface.extend_with_partial_interface(*partial_interface);
} }
interface.dictionaries.update(import.dictionaries); for (auto& dictionary : import.dictionaries) {
auto dictionary_copy = dictionary.value;
dictionary_copy.is_original_definition = false;
interface.dictionaries.set(dictionary.key, move(dictionary_copy));
}
for (auto& partial_dictionary : import.partial_dictionaries) { for (auto& partial_dictionary : import.partial_dictionaries) {
auto& it = interface.partial_dictionaries.ensure(partial_dictionary.key); auto& it = interface.partial_dictionaries.ensure(partial_dictionary.key);

View file

@ -51,7 +51,7 @@ private:
void parse_enumeration(HashMap<ByteString, ByteString>, Interface&); void parse_enumeration(HashMap<ByteString, ByteString>, Interface&);
void parse_typedef(Interface&); void parse_typedef(Interface&);
void parse_interface_mixin(Interface&); void parse_interface_mixin(Interface&);
void parse_dictionary(Interface&); void parse_dictionary(HashMap<ByteString, ByteString> extended_attributes, Interface&);
void parse_callback_function(HashMap<ByteString, ByteString>& extended_attributes, Interface&); void parse_callback_function(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_constructor(HashMap<ByteString, ByteString>& extended_attributes, Interface&); void parse_constructor(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_getter(HashMap<ByteString, ByteString>& extended_attributes, Interface&); void parse_getter(HashMap<ByteString, ByteString>& extended_attributes, Interface&);

View file

@ -208,6 +208,8 @@ struct DictionaryMember {
struct Dictionary { struct Dictionary {
ByteString parent_name; ByteString parent_name;
Vector<DictionaryMember> members; Vector<DictionaryMember> members;
HashMap<ByteString, ByteString> extended_attributes;
bool is_original_definition { true };
}; };
struct Typedef { struct Typedef {
@ -336,7 +338,7 @@ public:
bool will_generate_code() const bool will_generate_code() const
{ {
return !name.is_empty() || any_of(enumerations, [](auto& entry) { return entry.value.is_original_definition; }); return !name.is_empty() || any_of(dictionaries, [](auto& entry) { return entry.value.is_original_definition; }) || any_of(enumerations, [](auto& entry) { return entry.value.is_original_definition; });
} }
void extend_with_partial_interface(Interface const&); void extend_with_partial_interface(Interface const&);

View file

@ -2953,6 +2953,33 @@ static ByteString get_best_value_for_underlying_enum_type(size_t size)
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
static void generate_dictionaries(SourceGenerator& generator, IDL::Interface const& interface)
{
for (auto const& it : interface.dictionaries) {
if (!it.value.is_original_definition)
continue;
if (!it.value.extended_attributes.contains("GenerateToValue"sv))
continue;
auto dictionary_generator = generator.fork();
dictionary_generator.set("dictionary.name", make_input_acceptable_cpp(it.key));
dictionary_generator.set("dictionary.name:snakecase", make_input_acceptable_cpp(it.key.to_snakecase()));
dictionary_generator.append(R"~~~(
JS::Value @dictionary.name:snakecase@_to_value(JS::Realm&, @dictionary.name@ const&);
JS::Value @dictionary.name:snakecase@_to_value(JS::Realm& realm, @dictionary.name@ const& dictionary)
{
auto& vm = realm.vm();
@dictionary.name@ copy = dictionary;
)~~~");
// FIXME: Support generating wrap statements for lvalues and get rid of the copy above
auto dictionary_type = adopt_ref(*new Type(it.key, false));
generate_wrap_statement(dictionary_generator, "copy", dictionary_type, interface, "return"sv);
dictionary_generator.append(R"~~~(
}
)~~~");
}
}
static void generate_enumerations(HashMap<ByteString, Enumeration> const& enumerations, StringBuilder& builder) static void generate_enumerations(HashMap<ByteString, Enumeration> const& enumerations, StringBuilder& builder)
{ {
SourceGenerator generator { builder }; SourceGenerator generator { builder };
@ -4748,6 +4775,8 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::clear)
)~~~"); )~~~");
} }
} }
generate_dictionaries(generator, interface);
} }
void generate_namespace_header(IDL::Interface const& interface, StringBuilder& builder) void generate_namespace_header(IDL::Interface const& interface, StringBuilder& builder)