ladybird/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/main.cpp
DexesTTP 2ab8d474c6 Lagom: Fix leaks in the IDL Wrapper generator
By using RefPtrs to handle interfaces, the IDL parser could store cyclic
references to interfaces that import each other. One main example is the
"EventTarget.idl" and the "AbortSignal.idl" files, which both reference
each other. This caused huge amounts of memory not to be freed on exit.

To fix this, the parsed IDL interfaces are now stored in a HashTable of
NonnullOwnPtr<Interface>, which serves as the sole reference for every
parsed interface. All other usages of the Interface are changed to use
references instead of RefPtrs, or occasionally as raw pointers where
references don't fit inside the data structures.

This new HashTable is static, and as such will automatically be freed
prior to exiting the generator. This ensures that the code generator
properly cleans up after itself.

With this change, The IDL code generators can properly run on Lagom when
compiled with the -DENABLE_ADDRESS_SANITIZER=ON flag, and gets compiled
properly on the CI :^)
2022-05-25 22:25:09 +01:00

168 lines
6.8 KiB
C++

/*
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "IDLParser.h"
#include "IDLTypes.h"
#include <AK/Debug.h>
#include <AK/LexicalPath.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
extern Vector<StringView> s_header_search_paths;
namespace IDL {
void generate_constructor_header(IDL::Interface const&);
void generate_constructor_implementation(IDL::Interface const&);
void generate_prototype_header(IDL::Interface const&);
void generate_prototype_implementation(IDL::Interface const&);
void generate_header(IDL::Interface const&);
void generate_implementation(IDL::Interface const&);
void generate_iterator_prototype_header(IDL::Interface const&);
void generate_iterator_prototype_implementation(IDL::Interface const&);
void generate_iterator_header(IDL::Interface const&);
void generate_iterator_implementation(IDL::Interface const&);
}
int main(int argc, char** argv)
{
Core::ArgsParser args_parser;
StringView path = nullptr;
StringView import_base_path = nullptr;
bool header_mode = false;
bool implementation_mode = false;
bool constructor_header_mode = false;
bool constructor_implementation_mode = false;
bool prototype_header_mode = false;
bool prototype_implementation_mode = false;
bool iterator_header_mode = false;
bool iterator_implementation_mode = false;
bool iterator_prototype_header_mode = false;
bool iterator_prototype_implementation_mode = false;
args_parser.add_option(header_mode, "Generate the wrapper .h file", "header", 'H');
args_parser.add_option(implementation_mode, "Generate the wrapper .cpp file", "implementation", 'I');
args_parser.add_option(constructor_header_mode, "Generate the constructor .h file", "constructor-header", 'C');
args_parser.add_option(constructor_implementation_mode, "Generate the constructor .cpp file", "constructor-implementation", 'O');
args_parser.add_option(prototype_header_mode, "Generate the prototype .h file", "prototype-header", 'P');
args_parser.add_option(prototype_implementation_mode, "Generate the prototype .cpp file", "prototype-implementation", 'R');
args_parser.add_option(iterator_header_mode, "Generate the iterator wrapper .h file", "iterator-header", 0);
args_parser.add_option(iterator_implementation_mode, "Generate the iterator wrapper .cpp file", "iterator-implementation", 0);
args_parser.add_option(iterator_prototype_header_mode, "Generate the iterator prototype .h file", "iterator-prototype-header", 0);
args_parser.add_option(iterator_prototype_implementation_mode, "Generate the iterator prototype .cpp file", "iterator-prototype-implementation", 0);
args_parser.add_option(Core::ArgsParser::Option {
.requires_argument = true,
.help_string = "Add a header search path passed to the compiler",
.long_name = "header-include-path",
.short_name = 'i',
.value_name = "path",
.accept_value = [&](char const* s) {
s_header_search_paths.append(s);
return true;
},
});
args_parser.add_positional_argument(path, "IDL file", "idl-file");
args_parser.add_positional_argument(import_base_path, "Import base path", "import-base-path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
auto file_or_error = Core::File::open(path, Core::OpenMode::ReadOnly);
if (file_or_error.is_error()) {
warnln("Failed to open {}: {}", path, file_or_error.error());
return 1;
}
LexicalPath lexical_path(path);
auto& namespace_ = lexical_path.parts_view().at(lexical_path.parts_view().size() - 2);
auto data = file_or_error.value()->read_all();
if (import_base_path.is_null())
import_base_path = lexical_path.dirname();
auto& interface = IDL::Parser(path, data, import_base_path).parse();
if (namespace_.is_one_of("Crypto", "CSS", "DOM", "Encoding", "HTML", "UIEvents", "Geometry", "HighResolutionTime", "IntersectionObserver", "NavigationTiming", "RequestIdleCallback", "ResizeObserver", "SVG", "Selection", "URL", "WebSockets", "XHR")) {
StringBuilder builder;
builder.append(namespace_);
builder.append("::");
builder.append(interface.name);
interface.fully_qualified_name = builder.to_string();
} else {
interface.fully_qualified_name = interface.name;
}
if constexpr (WRAPPER_GENERATOR_DEBUG) {
dbgln("Attributes:");
for (auto& attribute : interface.attributes) {
dbgln(" {}{}{} {}",
attribute.readonly ? "readonly " : "",
attribute.type->name,
attribute.type->nullable ? "?" : "",
attribute.name);
}
dbgln("Functions:");
for (auto& function : interface.functions) {
dbgln(" {}{} {}",
function.return_type->name,
function.return_type->nullable ? "?" : "",
function.name);
for (auto& parameter : function.parameters) {
dbgln(" {}{} {}",
parameter.type->name,
parameter.type->nullable ? "?" : "",
parameter.name);
}
}
dbgln("Static Functions:");
for (auto& function : interface.static_functions) {
dbgln(" static {}{} {}",
function.return_type->name,
function.return_type->nullable ? "?" : "",
function.name);
for (auto& parameter : function.parameters) {
dbgln(" {}{} {}",
parameter.type->name,
parameter.type->nullable ? "?" : "",
parameter.name);
}
}
}
if (header_mode)
IDL::generate_header(interface);
if (implementation_mode)
IDL::generate_implementation(interface);
if (constructor_header_mode)
IDL::generate_constructor_header(interface);
if (constructor_implementation_mode)
IDL::generate_constructor_implementation(interface);
if (prototype_header_mode)
IDL::generate_prototype_header(interface);
if (prototype_implementation_mode)
IDL::generate_prototype_implementation(interface);
if (iterator_header_mode)
IDL::generate_iterator_header(interface);
if (iterator_implementation_mode)
IDL::generate_iterator_implementation(interface);
if (iterator_prototype_header_mode)
IDL::generate_iterator_prototype_header(interface);
if (iterator_prototype_implementation_mode)
IDL::generate_iterator_prototype_implementation(interface);
return 0;
}