summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Wilde <lukew@serenityos.org>2021-09-03 21:20:57 +0100
committerAndreas Kling <kling@serenityos.org>2021-09-03 23:11:58 +0200
commited97ee902bbda9c1a8f62da956fe684371426026 (patch)
treedb0444e4dfddb891f94bd6e1b122fe8c3f361e64
parent678d958be2f311a37999597c94344f1ac30baf68 (diff)
downloadserenity-ed97ee902bbda9c1a8f62da956fe684371426026.zip
LibWeb: Add support for IDL static functions
-rw-r--r--Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp292
1 files changed, 187 insertions, 105 deletions
diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp
index 89cc66fcfc..2d302aa4a9 100644
--- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp
+++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp
@@ -1,6 +1,7 @@
/*
* 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>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -137,6 +138,7 @@ struct Interface {
Vector<Constant> constants;
Vector<Constructor> constructors;
Vector<Function> functions;
+ Vector<Function> static_functions;
// Added for convenience after parsing
String wrapper_class;
@@ -300,6 +302,12 @@ static OwnPtr<Interface> parse_interface(StringView filename, StringView const&
};
auto parse_function = [&](HashMap<String, String>& extended_attributes) {
+ bool static_ = false;
+ if (lexer.consume_specific("static")) {
+ static_ = true;
+ consume_whitespace();
+ }
+
auto return_type = parse_type();
consume_whitespace();
auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == '('; });
@@ -310,7 +318,10 @@ static OwnPtr<Interface> parse_interface(StringView filename, StringView const&
consume_whitespace();
assert_specific(';');
- interface->functions.append(Function { return_type, name, move(parameters), move(extended_attributes) });
+ if (!static_)
+ interface->functions.append(Function { return_type, name, move(parameters), move(extended_attributes) });
+ else
+ interface->static_functions.append(Function { return_type, name, move(parameters), move(extended_attributes) });
};
auto parse_constructor = [&] {
@@ -445,6 +456,20 @@ int main(int argc, char** argv)
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)
@@ -747,6 +772,135 @@ static void generate_arguments(SourceGenerator& generator, Vector<IDL::Parameter
arguments_builder.join(", ", parameter_names);
}
+static void generate_return_statement(SourceGenerator& generator, IDL::Type const& return_type)
+{
+ auto scoped_generator = generator.fork();
+ scoped_generator.set("return_type", return_type.name);
+
+ if (return_type.name == "undefined") {
+ scoped_generator.append(R"~~~(
+ return JS::js_undefined();
+)~~~");
+ return;
+ }
+
+ if (return_type.nullable) {
+ if (return_type.is_string()) {
+ scoped_generator.append(R"~~~(
+ if (retval.is_null())
+ return JS::js_null();
+)~~~");
+ } else {
+ scoped_generator.append(R"~~~(
+ if (!retval)
+ return JS::js_null();
+)~~~");
+ }
+ }
+
+ if (return_type.is_string()) {
+ scoped_generator.append(R"~~~(
+ return JS::js_string(vm, retval);
+)~~~");
+ } else if (return_type.name == "ArrayFromVector") {
+ // FIXME: Remove this fake type hack once it's no longer needed.
+ // Basically once we have NodeList we can throw this out.
+ scoped_generator.append(R"~~~(
+ auto* new_array = JS::Array::create(global_object, 0);
+ for (auto& element : retval)
+ new_array->indexed_properties().append(wrap(global_object, element));
+
+ return new_array;
+)~~~");
+ } else if (return_type.name == "boolean" || return_type.name == "double") {
+ scoped_generator.append(R"~~~(
+ return JS::Value(retval);
+)~~~");
+ } else if (return_type.name == "short" || return_type.name == "unsigned short" || return_type.name == "long" || return_type.name == "unsigned long") {
+ scoped_generator.append(R"~~~(
+ return JS::Value((i32)retval);
+)~~~");
+ } else if (return_type.name == "Uint8ClampedArray") {
+ scoped_generator.append(R"~~~(
+ return retval;
+)~~~");
+ } else if (return_type.name == "EventHandler") {
+ scoped_generator.append(R"~~~(
+ if (retval.callback.is_null())
+ return JS::js_null();
+
+ return retval.callback.cell();
+)~~~");
+ } else {
+ scoped_generator.append(R"~~~(
+ return wrap(global_object, const_cast<@return_type@&>(*retval));
+)~~~");
+ }
+}
+
+enum class StaticFunction {
+ No,
+ Yes,
+};
+
+static void generate_function(SourceGenerator& generator, IDL::Function const& function, StaticFunction is_static_function, String const& class_name, String const& interface_fully_qualified_name)
+{
+ auto function_generator = generator.fork();
+ function_generator.set("class_name", class_name);
+ function_generator.set("interface_fully_qualified_name", interface_fully_qualified_name);
+ function_generator.set("function.name", function.name);
+ function_generator.set("function.name:snakecase", function.name.to_snakecase());
+
+ if (function.extended_attributes.contains("ImplementedAs")) {
+ auto implemented_as = function.extended_attributes.get("ImplementedAs").value();
+ function_generator.set("function.cpp_name", implemented_as);
+ } else {
+ function_generator.set("function.cpp_name", function.name.to_snakecase());
+ }
+
+ function_generator.append(R"~~~(
+JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
+{
+)~~~");
+
+ if (is_static_function == StaticFunction::No) {
+ function_generator.append(R"~~~(
+ auto* impl = impl_from(vm, global_object);
+ if (!impl)
+ return {};
+)~~~");
+ }
+
+ generate_argument_count_check(generator, function);
+
+ StringBuilder arguments_builder;
+ generate_arguments(generator, function.parameters, arguments_builder);
+ function_generator.set(".arguments", arguments_builder.string_view());
+
+ if (is_static_function == StaticFunction::No) {
+ function_generator.append(R"~~~(
+ auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->@function.cpp_name@(@.arguments@); });
+)~~~");
+ } else {
+ function_generator.append(R"~~~(
+ auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return @interface_fully_qualified_name@::@function.cpp_name@(@.arguments@); });
+)~~~");
+ }
+
+ function_generator.append(R"~~~(
+ if (should_return_empty(result))
+ return JS::Value();
+
+ [[maybe_unused]] auto retval = result.release_value();
+)~~~");
+
+ generate_return_statement(generator, function.return_type);
+
+ function_generator.append(R"~~~(
+}
+)~~~");
+}
+
static void generate_header(IDL::Interface const& interface)
{
StringBuilder builder;
@@ -992,6 +1146,17 @@ public:
private:
virtual bool has_constructor() const override { return true; }
+)~~~");
+
+ for (auto& function : interface.static_functions) {
+ auto function_generator = generator.fork();
+ function_generator.set("function.name:snakecase", function.name.to_snakecase());
+ function_generator.append(R"~~~(
+ JS_DECLARE_NATIVE_FUNCTION(@function.name:snakecase@);
+)~~~");
+ }
+
+ generator.append(R"~~~(
};
} // namespace Web::Bindings
@@ -1134,9 +1299,28 @@ define_direct_property("@constant.name@", JS::Value((i32)@constant.value@), JS::
)~~~");
}
+ // https://heycam.github.io/webidl/#es-operations
+ for (auto& function : interface.static_functions) {
+ auto function_generator = generator.fork();
+ function_generator.set("function.name", function.name);
+ function_generator.set("function.name:snakecase", function.name.to_snakecase());
+ function_generator.set("function.length", String::number(function.length()));
+
+ function_generator.append(R"~~~(
+ define_native_function("@function.name@", @function.name:snakecase@, @function.length@, default_attributes);
+)~~~");
+ }
+
generator.append(R"~~~(
}
+)~~~");
+
+ // Implementation: Static Functions
+ for (auto& function : interface.static_functions) {
+ generate_function(generator, function, StaticFunction::Yes, interface.constructor_class, interface.fully_qualified_name);
+ }
+ generator.append(R"~~~(
} // namespace Web::Bindings
)~~~");
@@ -1396,71 +1580,6 @@ static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_ob
)~~~");
}
- auto generate_return_statement = [&](auto& return_type) {
- auto scoped_generator = generator.fork();
- scoped_generator.set("return_type", return_type.name);
-
- if (return_type.name == "undefined") {
- scoped_generator.append(R"~~~(
- return JS::js_undefined();
-)~~~");
- return;
- }
-
- if (return_type.nullable) {
- if (return_type.is_string()) {
- scoped_generator.append(R"~~~(
- if (retval.is_null())
- return JS::js_null();
-)~~~");
- } else {
- scoped_generator.append(R"~~~(
- if (!retval)
- return JS::js_null();
-)~~~");
- }
- }
-
- if (return_type.is_string()) {
- scoped_generator.append(R"~~~(
- return JS::js_string(vm, retval);
-)~~~");
- } else if (return_type.name == "ArrayFromVector") {
- // FIXME: Remove this fake type hack once it's no longer needed.
- // Basically once we have NodeList we can throw this out.
- scoped_generator.append(R"~~~(
- auto* new_array = JS::Array::create(global_object, 0);
- for (auto& element : retval)
- new_array->indexed_properties().append(wrap(global_object, element));
-
- return new_array;
-)~~~");
- } else if (return_type.name == "boolean" || return_type.name == "double") {
- scoped_generator.append(R"~~~(
- return JS::Value(retval);
-)~~~");
- } else if (return_type.name == "short" || return_type.name == "unsigned short" || return_type.name == "long" || return_type.name == "unsigned long") {
- scoped_generator.append(R"~~~(
- return JS::Value((i32)retval);
-)~~~");
- } else if (return_type.name == "Uint8ClampedArray") {
- scoped_generator.append(R"~~~(
- return retval;
-)~~~");
- } else if (return_type.name == "EventHandler") {
- scoped_generator.append(R"~~~(
- if (retval.callback.is_null())
- return JS::js_null();
-
- return retval.callback.cell();
-)~~~");
- } else {
- scoped_generator.append(R"~~~(
- return wrap(global_object, const_cast<@return_type@&>(*retval));
-)~~~");
- }
- };
-
for (auto& attribute : interface.attributes) {
auto attribute_generator = generator.fork();
attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name);
@@ -1516,7 +1635,7 @@ JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@attribute.getter_callback@)
)~~~");
}
- generate_return_statement(attribute.type);
+ generate_return_statement(generator, attribute.type);
attribute_generator.append(R"~~~(
}
@@ -1563,44 +1682,7 @@ JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@attribute.setter_callback@)
// Implementation: Functions
for (auto& function : interface.functions) {
- auto function_generator = generator.fork();
- function_generator.set("function.name", function.name);
- function_generator.set("function.name:snakecase", function.name.to_snakecase());
-
- if (function.extended_attributes.contains("ImplementedAs")) {
- auto implemented_as = function.extended_attributes.get("ImplementedAs").value();
- function_generator.set("function.cpp_name", implemented_as);
- } else {
- function_generator.set("function.cpp_name", function.name.to_snakecase());
- }
-
- function_generator.append(R"~~~(
-JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@function.name:snakecase@)
-{
- auto* impl = impl_from(vm, global_object);
- if (!impl)
- return {};
-)~~~");
-
- generate_argument_count_check(generator, function);
-
- StringBuilder arguments_builder;
- generate_arguments(generator, function.parameters, arguments_builder);
- function_generator.set(".arguments", arguments_builder.string_view());
-
- function_generator.append(R"~~~(
- auto result = throw_dom_exception_if_needed(vm, global_object, [&] { return impl->@function.cpp_name@(@.arguments@); });
- if (should_return_empty(result))
- return JS::Value();
-
- [[maybe_unused]] auto retval = result.release_value();
-)~~~");
-
- generate_return_statement(function.return_type);
-
- function_generator.append(R"~~~(
-}
-)~~~");
+ generate_function(generator, function, StaticFunction::No, interface.prototype_class, interface.fully_qualified_name);
}
generator.append(R"~~~(