summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibWeb/CMakeLists.txt34
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp231
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());
+}