summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp')
-rw-r--r--Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp231
1 files changed, 231 insertions, 0 deletions
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());
+}