diff options
author | Andreas Kling <kling@serenityos.org> | 2021-01-18 08:35:46 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-18 12:18:29 +0100 |
commit | 252a98042d1f0bdf90df4adff55f3b34e8703098 (patch) | |
tree | cfed1d830c441ba9284f3b11b361b50643917482 /Userland/Libraries | |
parent | 81839ea1bd273786c944a0878e62ca5d326a295f (diff) | |
download | serenity-252a98042d1f0bdf90df4adff55f3b34e8703098.zip |
LibWeb: Generate constructor and prototype classes for IDL interfaces
This patch adds a FooPrototype and FooConstructor class for each IDL
interface we generate JS bindings for.
These classes are very primitive and don't do everything they should
yet, but we have to start somewhere. :^)
Work towards #4789
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibWeb/CMakeLists.txt | 34 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp | 231 |
2 files changed, 265 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 1131811c04..7e97391a76 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -226,6 +226,8 @@ endfunction(add_wrapper_sources) function(libweb_js_wrapper class) get_filename_component(basename ${class} NAME) add_wrapper_sources(Bindings/${basename}Wrapper.cpp Bindings/${basename}Wrapper.h) + add_wrapper_sources(Bindings/${basename}Constructor.cpp Bindings/${basename}Constructor.h) + add_wrapper_sources(Bindings/${basename}Prototype.cpp Bindings/${basename}Prototype.h) add_custom_command( OUTPUT Bindings/${basename}Wrapper.h COMMAND ${write_if_different} Bindings/${basename}Wrapper.h CodeGenerators/WrapperGenerator --header ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl @@ -240,8 +242,40 @@ function(libweb_js_wrapper class) DEPENDS WrapperGenerator MAIN_DEPENDENCY ${class}.idl ) + add_custom_command( + OUTPUT Bindings/${basename}Constructor.h + COMMAND ${write_if_different} Bindings/${basename}Constructor.h CodeGenerators/WrapperGenerator --constructor-header ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY ${class}.idl + ) + add_custom_command( + OUTPUT Bindings/${basename}Constructor.cpp + COMMAND ${write_if_different} Bindings/${basename}Constructor.cpp CodeGenerators/WrapperGenerator --constructor-implementation ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY ${class}.idl + ) + add_custom_command( + OUTPUT Bindings/${basename}Prototype.h + COMMAND ${write_if_different} Bindings/${basename}Prototype.h CodeGenerators/WrapperGenerator --prototype-header ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY ${class}.idl + ) + add_custom_command( + OUTPUT Bindings/${basename}Prototype.cpp + COMMAND ${write_if_different} Bindings/${basename}Prototype.cpp CodeGenerators/WrapperGenerator --prototype-implementation ${CMAKE_CURRENT_SOURCE_DIR}/${class}.idl + VERBATIM + DEPENDS WrapperGenerator + MAIN_DEPENDENCY ${class}.idl + ) add_custom_target(generate_${basename}Wrapper.h DEPENDS Bindings/${class}Wrapper.h) add_custom_target(generate_${basename}Wrapper.cpp DEPENDS Bindings/${class}Wrapper.cpp) + add_custom_target(generate_${basename}Constructor.h DEPENDS Bindings/${class}Constructor.h) + add_custom_target(generate_${basename}Constructor.cpp DEPENDS Bindings/${class}Constructor.cpp) + add_custom_target(generate_${basename}Prototype.h DEPENDS Bindings/${class}Prototype.h) + add_custom_target(generate_${basename}Prototype.cpp DEPENDS Bindings/${class}Prototype.cpp) endfunction() libweb_js_wrapper(DOM/CharacterData) diff --git a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp index dbedd118b5..48355de36c 100644 --- a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp +++ b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -158,6 +158,8 @@ struct Interface { String wrapper_class; String wrapper_base_class; String fully_qualified_name; + String constructor_class; + String prototype_class; }; static OwnPtr<Interface> parse_interface(StringView filename, const StringView& input) @@ -308,12 +310,18 @@ static OwnPtr<Interface> parse_interface(StringView filename, const StringView& interface->wrapper_class = String::formatted("{}Wrapper", interface->name); interface->wrapper_base_class = String::formatted("{}Wrapper", interface->parent_name.is_empty() ? String::empty() : interface->parent_name); + interface->constructor_class = String::formatted("{}Constructor", interface->name); + interface->prototype_class = String::formatted("{}Prototype", interface->name); return interface; } } +static void generate_constructor_header(const IDL::Interface&); +static void generate_constructor_implementation(const IDL::Interface&); +static void generate_prototype_header(const IDL::Interface&); +static void generate_prototype_implementation(const IDL::Interface&); static void generate_header(const IDL::Interface&); static void generate_implementation(const IDL::Interface&); @@ -323,8 +331,16 @@ int main(int argc, char** argv) const char* 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; 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_positional_argument(path, "IDL file", "idl-file"); args_parser.parse(argc, argv); @@ -379,6 +395,18 @@ int main(int argc, char** argv) if (implementation_mode) generate_implementation(*interface); + if (constructor_header_mode) + generate_constructor_header(*interface); + + if (constructor_implementation_mode) + generate_constructor_implementation(*interface); + + if (prototype_header_mode) + generate_prototype_header(*interface); + + if (prototype_implementation_mode) + generate_prototype_implementation(*interface); + return 0; } @@ -949,3 +977,206 @@ JS_DEFINE_NATIVE_FUNCTION(@wrapper_class@::@function.name:snakecase@) outln("{}", generator.as_string_view()); } + +static void generate_constructor_header(const IDL::Interface& interface) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.set("name", interface.name); + generator.set("fully_qualified_name", interface.fully_qualified_name); + generator.set("constructor_class", interface.constructor_class); + generator.set("constructor_class:snakecase", snake_name(interface.constructor_class)); + + generator.append(R"~~~( +#pragma once + +#include <LibJS/Runtime/NativeFunction.h> + +namespace Web::Bindings { + +class @constructor_class@ : public JS::NativeFunction { + JS_OBJECT(@constructor_class@, JS::NativeFunction); +public: + explicit @constructor_class@(JS::GlobalObject&); + virtual void initialize(JS::GlobalObject&) override; + virtual ~@constructor_class@() override; + + virtual JS::Value call() override; + virtual JS::Value construct(JS::Function& new_target) override; + +private: + virtual bool has_constructor() const override { return true; } +}; + +} // namespace Web::Bindings +)~~~"); + + outln("{}", generator.as_string_view()); +} + +void generate_constructor_implementation(const IDL::Interface& interface) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.set("name", interface.name); + generator.set("prototype_class", interface.prototype_class); + generator.set("wrapper_class", interface.wrapper_class); + generator.set("constructor_class", interface.constructor_class); + generator.set("prototype_class:snakecase", snake_name(interface.prototype_class)); + generator.set("fully_qualified_name", interface.fully_qualified_name); + + generator.append(R"~~~( +#include <LibJS/Heap/Heap.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Bindings/@constructor_class@.h> +#include <LibWeb/Bindings/@prototype_class@.h> +#include <LibWeb/Bindings/@wrapper_class@.h> +#include <LibWeb/Bindings/WindowObject.h> +#if __has_include(<LibWeb/DOM/@name@.h>) +# include <LibWeb/DOM/@name@.h> +#elif __has_include(<LibWeb/HTML/@name@.h>) +# include <LibWeb/HTML/@name@.h> +#elif __has_include(<LibWeb/UIEvents/@name@.h>) +# include <LibWeb/UIEvents/@name@.h> +#elif __has_include(<LibWeb/HighResolutionTime/@name@.h>) +# include <LibWeb/HighResolutionTime/@name@.h> +#elif __has_include(<LibWeb/SVG/@name@.h>) +# include <LibWeb/SVG/@name@.h> +#endif + +// FIXME: This is a total hack until we can figure out the namespace for a given type somehow. +using namespace Web::DOM; +using namespace Web::HTML; + +namespace Web::Bindings { + +@constructor_class@::@constructor_class@(JS::GlobalObject& global_object) + : NativeFunction(*global_object.function_prototype()) +{ +} + +@constructor_class@::~@constructor_class@() +{ +} + +JS::Value @constructor_class@::call() +{ + vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "@name@"); + return {}; +} + +JS::Value @constructor_class@::construct(Function&) +{ + return {}; +#if 0 + // FIXME: It would be cool to construct stuff! + auto& window = static_cast<WindowObject&>(global_object()); + return heap().allocate<@wrapper_class@>(window, window, @fully_qualified_name@::create(window.impl())); +#endif +} + +void @constructor_class@::initialize(JS::GlobalObject& global_object) +{ + auto& vm = this->vm(); + auto& window = static_cast<WindowObject&>(global_object); + [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable; + + NativeFunction::initialize(global_object); + define_property(vm.names.prototype, window.web_prototype("@prototype_class@"), 0); + define_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable); +} + +} // namespace Web::Bindings +)~~~"); + + outln("{}", generator.as_string_view()); +} + +static void generate_prototype_header(const IDL::Interface& interface) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.set("name", interface.name); + generator.set("fully_qualified_name", interface.fully_qualified_name); + generator.set("prototype_class", interface.prototype_class); + generator.set("prototype_class:snakecase", snake_name(interface.prototype_class)); + + generator.append(R"~~~( +#pragma once + +#include <LibJS/Runtime/Object.h> + +namespace Web::Bindings { + +class @prototype_class@ : public JS::Object { + JS_OBJECT(@prototype_class@, JS::Object); +public: + explicit @prototype_class@(JS::GlobalObject&); + virtual void initialize(JS::GlobalObject&) override; + virtual ~@prototype_class@() override; +}; + +} // namespace Web::Bindings + )~~~"); + + outln("{}", generator.as_string_view()); +} + +void generate_prototype_implementation(const IDL::Interface& interface) +{ + StringBuilder builder; + SourceGenerator generator { builder }; + + generator.set("name", interface.name); + generator.set("prototype_class", interface.prototype_class); + generator.set("wrapper_class", interface.wrapper_class); + generator.set("constructor_class", interface.constructor_class); + generator.set("prototype_class:snakecase", snake_name(interface.prototype_class)); + generator.set("fully_qualified_name", interface.fully_qualified_name); + + generator.append(R"~~~( +#include <AK/Function.h> +#include <LibJS/Runtime/Error.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibWeb/Bindings/@prototype_class@.h> +#if __has_include(<LibWeb/DOM/@name@.h>) +# include <LibWeb/DOM/@name@.h> +#elif __has_include(<LibWeb/HTML/@name@.h>) +# include <LibWeb/HTML/@name@.h> +#elif __has_include(<LibWeb/UIEvents/@name@.h>) +# include <LibWeb/UIEvents/@name@.h> +#elif __has_include(<LibWeb/HighResolutionTime/@name@.h>) +# include <LibWeb/HighResolutionTime/@name@.h> +#elif __has_include(<LibWeb/SVG/@name@.h>) +# include <LibWeb/SVG/@name@.h> +#endif + +// FIXME: This is a total hack until we can figure out the namespace for a given type somehow. +using namespace Web::DOM; +using namespace Web::HTML; + +namespace Web::Bindings { + +@prototype_class@::@prototype_class@(JS::GlobalObject& global_object) + : Object(*global_object.object_prototype()) +{ +} + +@prototype_class@::~@prototype_class@() +{ +} + +void @prototype_class@::initialize(JS::GlobalObject& global_object) +{ + [[maybe_unused]] auto& vm = this->vm(); + Object::initialize(global_object); +} + +} // namespace Web::Bindings +)~~~"); + + outln("{}", generator.as_string_view()); +} |