GMLCompiler: Use synthetic widget definition when original is missing

This makes it possible to use externally defined toplevel widgets that
have no C++ header defining them.
Note that this only allows widget-native properties on the object, as
the actual original definition is not available.
This commit is contained in:
Ali Mohammad Pur 2024-04-22 20:30:53 +02:00 committed by Andrew Kaster
commit 5c17b61378
Notes: sideshowbarker 2024-07-16 23:17:55 +09:00

View file

@ -102,10 +102,11 @@ static ErrorOr<String> include_path_for(StringView class_name, LexicalPath const
}
// Each entry is an include path, without the "#include" itself.
static ErrorOr<HashTable<String>> extract_necessary_includes(GUI::GML::Object const& gml_hierarchy, LexicalPath const& gml_file_name)
static ErrorOr<HashTable<String>> extract_necessary_includes(GUI::GML::Object const& gml_hierarchy, LexicalPath const& gml_file_name, bool is_root = false)
{
HashTable<String> necessary_includes;
TRY(necessary_includes.try_set(TRY(include_path_for(gml_hierarchy.name(), gml_file_name))));
if (!is_root)
TRY(necessary_includes.try_set(TRY(include_path_for(gml_hierarchy.name(), gml_file_name))));
if (gml_hierarchy.layout_object() != nullptr)
TRY(necessary_includes.try_set(TRY(include_path_for(gml_hierarchy.layout_object()->name(), gml_file_name))));
@ -126,6 +127,22 @@ static char const header[] = R"~~~(
)~~~";
static char const class_declaration[] = R"~~~(
// A barebones definition of @main_class_name@ used to emit the symbol try_create.
// Requirements:
// - Inherits from GUI::Widget (indirectly, is declared as 'class')
// - Has a default ctor
// - Has declared a compatible static ErrorOr<NonnullRefPtr<@pure_class_name@>> try_create().
namespace @class_namespace@ {
class @pure_class_name@ : public GUI::Widget {
public:
@pure_class_name@();
static ErrorOr<NonnullRefPtr<@pure_class_name@>> try_create();
};
}
)~~~";
static char const function_start[] = R"~~~(
// Creates a @main_class_name@ and initializes it.
// This function was auto-generated by the GML compiler.
@ -350,7 +367,7 @@ static ErrorOr<String> generate_cpp(NonnullRefPtr<GUI::GML::GMLFile> gml, Lexica
generator.append(header);
auto& main_class = gml->main_class();
auto necessary_includes = TRY(extract_necessary_includes(main_class, gml_file_name));
auto necessary_includes = TRY(extract_necessary_includes(main_class, gml_file_name, true));
static String const always_necessary_includes[] = {
"<AK/Error.h>"_string,
"<AK/JsonValue.h>"_string,
@ -367,8 +384,24 @@ static ErrorOr<String> generate_cpp(NonnullRefPtr<GUI::GML::GMLFile> gml, Lexica
for (auto const& include : necessary_includes)
generator.appendln(TRY(String::formatted("#include {}", include)));
auto main_file_header = TRY(include_path_for(main_class.name(), gml_file_name));
generator.appendln(TRY(String::formatted("#if __has_include({})", main_file_header)));
generator.appendln(TRY(String::formatted("#include {}", main_file_header)));
generator.appendln("#else");
// FIXME: Use a UTF-8 aware function once possible.
auto ns_position = main_class.name().find_last("::"sv);
auto ns = main_class.name().substring_view(0, ns_position.value_or(0));
auto pure_class_name = main_class.name().substring_view(ns_position.map([](auto x) { return x + 2; }).value_or(0));
generator.set("class_namespace", ns);
generator.set("pure_class_name", pure_class_name);
generator.set("main_class_name", main_class.name());
generator.append(class_declaration);
generator.appendln("#endif // __has_include(...)");
generator.append(function_start);
TRY(generate_loader_for_object(main_class, generator.fork(), "main_object"_string, 2, UseObjectConstructor::No));