From 630cbc947a511a90bdb1664957354ee6a45cb3c5 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 18 Jan 2021 11:36:34 +0100 Subject: [PATCH] LibWeb: Construct the IDL interface prototype chains automatically Have each IDL prototype trigger the construction of its own prototype. --- .../Libraries/LibWeb/Bindings/WindowObject.h | 25 +++++++++++++++++-- .../LibWeb/Bindings/WindowObjectHelper.h | 10 +++----- .../CodeGenerators/WrapperGenerator.cpp | 23 +++++++++++++++-- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObject.h b/Userland/Libraries/LibWeb/Bindings/WindowObject.h index de26fb2a18..29d80a196c 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObject.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObject.h @@ -55,8 +55,29 @@ public: JS::Object* web_prototype(const String& class_name) { return m_prototypes.get(class_name).value_or(nullptr); } JS::NativeFunction* web_constructor(const String& class_name) { return m_constructors.get(class_name).value_or(nullptr); } - void set_web_prototype(const String& class_name, JS::Object* prototype) { m_prototypes.set(class_name, prototype); } - void set_web_constructor(const String& class_name, JS::NativeFunction* constructor) { m_constructors.set(class_name, constructor); } + + template + JS::Object& ensure_web_prototype(const String& class_name) + { + auto it = m_prototypes.find(class_name); + if (it != m_prototypes.end()) + return *it->value; + auto* prototype = heap().allocate(*this, *this); + m_prototypes.set(class_name, prototype); + return *prototype; + } + + template + JS::NativeFunction& ensure_web_constructor(const String& class_name) + { + auto it = m_constructors.find(class_name); + if (it != m_constructors.end()) + return *it->value; + auto* constructor = heap().allocate(*this, *this); + m_constructors.set(class_name, constructor); + define_property(class_name, constructor, JS::Attribute::Writable | JS::Attribute::Configurable); + return *constructor; + } private: virtual const char* class_name() const override { return "WindowObject"; } diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index 72479c56d9..b37afe1894 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -217,13 +217,9 @@ #include #include -#define ADD_WINDOW_OBJECT_INTERFACE(name) \ - { \ - auto* prototype = heap().allocate(*this, *this); \ - name##Constructor* constructor = nullptr; \ - add_constructor(#name, constructor, prototype); \ - set_web_prototype(#name, prototype); \ - set_web_constructor(#name, constructor); \ +#define ADD_WINDOW_OBJECT_INTERFACE(name) \ + { \ + ensure_web_constructor(#name); \ } #define ADD_WINDOW_OBJECT_INTERFACES \ diff --git a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp index 27e0278b12..76f7107b53 100644 --- a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp +++ b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -160,6 +160,7 @@ struct Interface { String fully_qualified_name; String constructor_class; String prototype_class; + String prototype_base_class; }; static OwnPtr parse_interface(StringView filename, const StringView& input) @@ -312,6 +313,7 @@ static OwnPtr parse_interface(StringView filename, const StringView& 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); + interface->prototype_base_class = String::formatted("{}Prototype", interface->parent_name.is_empty() ? "Object" : interface->parent_name); return interface; } @@ -563,6 +565,7 @@ void generate_implementation(const IDL::Interface& interface) generator.set("name", interface.name); generator.set("wrapper_class", interface.wrapper_class); generator.set("wrapper_base_class", interface.wrapper_base_class); + generator.set("prototype_class", interface.prototype_class); generator.set("fully_qualified_name", interface.fully_qualified_name); generator.append(R"~~~( @@ -573,6 +576,7 @@ void generate_implementation(const IDL::Interface& interface) #include #include #include +#include #include #include #include @@ -614,7 +618,7 @@ namespace Web::Bindings { @wrapper_class@::@wrapper_class@(JS::GlobalObject& global_object, @fully_qualified_name@& impl) : @wrapper_base_class@(global_object, impl) { - set_prototype(static_cast(global_object).web_prototype("@name@")); + set_prototype(&static_cast(global_object).ensure_web_prototype<@prototype_class@>("@name@")); } )~~~"); } @@ -1086,7 +1090,7 @@ void @constructor_class@::initialize(JS::GlobalObject& 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.prototype, window.ensure_web_prototype<@prototype_class@>("@name@"), 0); define_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable); } @@ -1133,7 +1137,9 @@ void generate_prototype_implementation(const IDL::Interface& interface) SourceGenerator generator { builder }; generator.set("name", interface.name); + generator.set("parent_name", interface.parent_name); generator.set("prototype_class", interface.prototype_class); + generator.set("prototype_base_class", interface.prototype_base_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)); @@ -1144,6 +1150,10 @@ void generate_prototype_implementation(const IDL::Interface& interface) #include #include #include +#include +#if __has_include() +# include +#endif #if __has_include() # include #elif __has_include() @@ -1165,6 +1175,15 @@ namespace Web::Bindings { @prototype_class@::@prototype_class@(JS::GlobalObject& global_object) : Object(*global_object.object_prototype()) { +)~~~"); + + if (!interface.parent_name.is_empty()) { + generator.append(R"~~~( + set_prototype(&static_cast(global_object).ensure_web_prototype<@prototype_base_class@>("@parent_name@")); +)~~~"); + } + + generator.append(R"~~~( } @prototype_class@::~@prototype_class@()