mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
LibWeb: Add support for passing sequences into IDL functions
This commit is contained in:
parent
9773c3cabc
commit
fb7ca09f04
Notes:
sideshowbarker
2024-07-17 19:56:44 +09:00
Author: https://github.com/Lubrsi Commit: https://github.com/SerenityOS/serenity/commit/fb7ca09f048 Pull-request: https://github.com/SerenityOS/serenity/pull/12208 Reviewed-by: https://github.com/awesomekling
1 changed files with 202 additions and 48 deletions
|
@ -81,6 +81,17 @@ static size_t get_function_length(FunctionType& function)
|
|||
return length;
|
||||
}
|
||||
|
||||
enum class SequenceStorageType {
|
||||
Vector, // Used to safely store non-JS values
|
||||
MarkedValueList, // Used to safely store JS::Value
|
||||
MarkedVector, // Used to safely store anything that inherits JS::Cell, e.g. JS::Object
|
||||
};
|
||||
|
||||
struct CppType {
|
||||
String name;
|
||||
SequenceStorageType sequence_storage_type;
|
||||
};
|
||||
|
||||
struct Type : public RefCounted<Type> {
|
||||
Type() = default;
|
||||
|
||||
|
@ -97,20 +108,6 @@ struct Type : public RefCounted<Type> {
|
|||
bool is_string() const { return name.is_one_of("ByteString", "CSSOMString", "DOMString", "USVString"); }
|
||||
};
|
||||
|
||||
struct ParameterizedType : public Type {
|
||||
ParameterizedType() = default;
|
||||
|
||||
ParameterizedType(String name, bool nullable, NonnullRefPtrVector<Type> parameters)
|
||||
: Type(move(name), nullable)
|
||||
, parameters(move(parameters))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ParameterizedType() override = default;
|
||||
|
||||
NonnullRefPtrVector<Type> parameters;
|
||||
};
|
||||
|
||||
struct Parameter {
|
||||
NonnullRefPtr<Type> type;
|
||||
String name;
|
||||
|
@ -166,6 +163,22 @@ struct Dictionary {
|
|||
Vector<DictionaryMember> members;
|
||||
};
|
||||
|
||||
struct ParameterizedType : public Type {
|
||||
ParameterizedType() = default;
|
||||
|
||||
ParameterizedType(String name, bool nullable, NonnullRefPtrVector<Type> parameters)
|
||||
: Type(move(name), nullable)
|
||||
, parameters(move(parameters))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ParameterizedType() override = default;
|
||||
|
||||
NonnullRefPtrVector<Type> parameters;
|
||||
|
||||
void generate_sequence_from_iterable(SourceGenerator& generator, String const& cpp_name, String const& iterable_cpp_name, String const& iterator_method_cpp_name, HashMap<String, Dictionary> const& dictionaries, size_t recursion_depth) const;
|
||||
};
|
||||
|
||||
struct Interface {
|
||||
String name;
|
||||
String parent_name;
|
||||
|
@ -752,6 +765,97 @@ static NonnullOwnPtr<Interface> parse_interface(StringView filename, StringView
|
|||
return interface;
|
||||
}
|
||||
|
||||
static bool is_wrappable_type(Type const& type)
|
||||
{
|
||||
if (type.name == "Node")
|
||||
return true;
|
||||
if (type.name == "Document")
|
||||
return true;
|
||||
if (type.name == "Text")
|
||||
return true;
|
||||
if (type.name == "DocumentType")
|
||||
return true;
|
||||
if (type.name.ends_with("Element"))
|
||||
return true;
|
||||
if (type.name.ends_with("Event"))
|
||||
return true;
|
||||
if (type.name == "ImageData")
|
||||
return true;
|
||||
if (type.name == "Window")
|
||||
return true;
|
||||
if (type.name == "Range")
|
||||
return true;
|
||||
if (type.name == "Selection")
|
||||
return true;
|
||||
if (type.name == "Attribute")
|
||||
return true;
|
||||
if (type.name == "NamedNodeMap")
|
||||
return true;
|
||||
if (type.name == "TextMetrics")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static StringView sequence_storage_type_to_cpp_storage_type_name(SequenceStorageType sequence_storage_type)
|
||||
{
|
||||
switch (sequence_storage_type) {
|
||||
case SequenceStorageType::Vector:
|
||||
return "Vector"sv;
|
||||
case SequenceStorageType::MarkedValueList:
|
||||
return "JS::MarkedValueList"sv;
|
||||
case SequenceStorageType::MarkedVector:
|
||||
return "JS::MarkedVector"sv;
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
static CppType idl_type_name_to_cpp_type(Type const& type)
|
||||
{
|
||||
if (is_wrappable_type(type)) {
|
||||
if (type.nullable)
|
||||
return { .name = String::formatted("RefPtr<{}>", type.name), .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
return { .name = String::formatted("NonnullRefPtr<{}>", type.name), .sequence_storage_type = SequenceStorageType::Vector };
|
||||
}
|
||||
|
||||
if (type.is_string())
|
||||
return { .name = "String", .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
if (type.name == "double" && !type.nullable)
|
||||
return { .name = "double", .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
if (type.name == "boolean" && !type.nullable)
|
||||
return { .name = "bool", .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
if (type.name == "unsigned long" && !type.nullable)
|
||||
return { .name = "u32", .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
if (type.name == "unsigned short" && !type.nullable)
|
||||
return { .name = "u16", .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
if (type.name == "long" && !type.nullable)
|
||||
return { .name = "i32", .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
if (type.name == "any")
|
||||
return { .name = "JS::Value", .sequence_storage_type = SequenceStorageType::MarkedValueList };
|
||||
|
||||
if (type.name == "sequence") {
|
||||
auto& parameterized_type = verify_cast<ParameterizedType>(type);
|
||||
auto& sequence_type = parameterized_type.parameters.first();
|
||||
auto sequence_cpp_type = idl_type_name_to_cpp_type(sequence_type);
|
||||
auto storage_type_name = sequence_storage_type_to_cpp_storage_type_name(sequence_cpp_type.sequence_storage_type);
|
||||
|
||||
if (sequence_cpp_type.sequence_storage_type == SequenceStorageType::MarkedValueList || sequence_cpp_type.sequence_storage_type == SequenceStorageType::MarkedVector)
|
||||
return { .name = storage_type_name, .sequence_storage_type = SequenceStorageType::Vector };
|
||||
|
||||
return { .name = String::formatted("{}<{}>", storage_type_name, sequence_cpp_type.name), .sequence_storage_type = SequenceStorageType::Vector };
|
||||
}
|
||||
|
||||
dbgln("Unimplemented type for idl_type_name_to_cpp_type: {}{}", type.name, type.nullable ? "?" : "");
|
||||
TODO();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void generate_constructor_header(IDL::Interface const&);
|
||||
|
@ -914,42 +1018,12 @@ static bool should_emit_wrapper_factory(IDL::Interface const& interface)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool is_wrappable_type(IDL::Type const& type)
|
||||
{
|
||||
if (type.name == "Node")
|
||||
return true;
|
||||
if (type.name == "Document")
|
||||
return true;
|
||||
if (type.name == "Text")
|
||||
return true;
|
||||
if (type.name == "DocumentType")
|
||||
return true;
|
||||
if (type.name.ends_with("Element"))
|
||||
return true;
|
||||
if (type.name.ends_with("Event"))
|
||||
return true;
|
||||
if (type.name == "ImageData")
|
||||
return true;
|
||||
if (type.name == "Window")
|
||||
return true;
|
||||
if (type.name == "Range")
|
||||
return true;
|
||||
if (type.name == "Selection")
|
||||
return true;
|
||||
if (type.name == "Attribute")
|
||||
return true;
|
||||
if (type.name == "NamedNodeMap")
|
||||
return true;
|
||||
if (type.name == "TextMetrics")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename ParameterType>
|
||||
static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter, String const& js_name, String const& js_suffix, String const& cpp_name, HashMap<String, IDL::Dictionary> const& dictionaries, bool legacy_null_to_empty_string = false, bool optional = false, Optional<String> optional_default_value = {}, bool variadic = false)
|
||||
static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter, String const& js_name, String const& js_suffix, String const& cpp_name, HashMap<String, IDL::Dictionary> const& dictionaries, bool legacy_null_to_empty_string = false, bool optional = false, Optional<String> optional_default_value = {}, bool variadic = false, size_t recursion_depth = 0)
|
||||
{
|
||||
auto scoped_generator = generator.fork();
|
||||
scoped_generator.set("cpp_name", make_input_acceptable_cpp(cpp_name));
|
||||
auto acceptable_cpp_name = make_input_acceptable_cpp(cpp_name);
|
||||
scoped_generator.set("cpp_name", acceptable_cpp_name);
|
||||
scoped_generator.set("js_name", js_name);
|
||||
scoped_generator.set("js_suffix", js_suffix);
|
||||
scoped_generator.set("legacy_null_to_empty_string", legacy_null_to_empty_string ? "true" : "false");
|
||||
|
@ -1028,7 +1102,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
|
|||
auto @cpp_name@ = adopt_ref(*new EventListener(JS::make_handle(&@js_name@@js_suffix@.as_function())));
|
||||
)~~~");
|
||||
}
|
||||
} else if (is_wrappable_type(*parameter.type)) {
|
||||
} else if (IDL::is_wrappable_type(*parameter.type)) {
|
||||
if (!parameter.type->nullable) {
|
||||
scoped_generator.append(R"~~~(
|
||||
auto @cpp_name@_object = TRY(@js_name@@js_suffix@.to_object(global_object));
|
||||
|
@ -1222,6 +1296,29 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
|
|||
VERIFY(dictionaries.contains(current_dictionary->parent_name));
|
||||
current_dictionary = &dictionaries.find(current_dictionary->parent_name)->value;
|
||||
}
|
||||
} else if (parameter.type->name == "sequence") {
|
||||
// https://webidl.spec.whatwg.org/#es-sequence
|
||||
|
||||
auto sequence_generator = scoped_generator.fork();
|
||||
auto& parameterized_type = verify_cast<IDL::ParameterizedType>(*parameter.type);
|
||||
sequence_generator.set("recursion_depth", String::number(recursion_depth));
|
||||
|
||||
// An ECMAScript value V is converted to an IDL sequence<T> value as follows:
|
||||
// 1. If Type(V) is not Object, throw a TypeError.
|
||||
// 2. Let method be ? GetMethod(V, @@iterator).
|
||||
// 3. If method is undefined, throw a TypeError.
|
||||
// 4. Return the result of creating a sequence from V and method.
|
||||
|
||||
sequence_generator.append(R"~~~(
|
||||
if (!@js_name@@js_suffix@.is_object())
|
||||
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAnObject, @js_name@@js_suffix@.to_string_without_side_effects());
|
||||
|
||||
auto* iterator_method@recursion_depth@ = TRY(@js_name@@js_suffix@.get_method(global_object, *vm.well_known_symbol_iterator()));
|
||||
if (!iterator_method@recursion_depth@)
|
||||
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotIterable, @js_name@@js_suffix@.to_string_without_side_effects());
|
||||
)~~~");
|
||||
|
||||
parameterized_type.generate_sequence_from_iterable(sequence_generator, acceptable_cpp_name, String::formatted("{}{}", js_name, js_suffix), String::formatted("iterator_method{}", recursion_depth), dictionaries, recursion_depth + 1);
|
||||
} else {
|
||||
dbgln("Unimplemented JS-to-C++ conversion: {}", parameter.type->name);
|
||||
VERIFY_NOT_REACHED();
|
||||
|
@ -1275,6 +1372,61 @@ static void generate_arguments(SourceGenerator& generator, Vector<IDL::Parameter
|
|||
arguments_builder.join(", ", parameter_names);
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#create-sequence-from-iterable
|
||||
void IDL::ParameterizedType::generate_sequence_from_iterable(SourceGenerator& generator, String const& cpp_name, String const& iterable_cpp_name, String const& iterator_method_cpp_name, HashMap<String, IDL::Dictionary> const& dictionaries, size_t recursion_depth) const
|
||||
{
|
||||
auto sequence_generator = generator.fork();
|
||||
sequence_generator.set("cpp_name", cpp_name);
|
||||
sequence_generator.set("iterable_cpp_name", iterable_cpp_name);
|
||||
sequence_generator.set("iterator_method_cpp_name", iterator_method_cpp_name);
|
||||
sequence_generator.set("recursion_depth", String::number(recursion_depth));
|
||||
auto sequence_cpp_type = idl_type_name_to_cpp_type(parameters.first());
|
||||
sequence_generator.set("sequence.type", sequence_cpp_type.name);
|
||||
sequence_generator.set("sequence.storage_type", sequence_storage_type_to_cpp_storage_type_name(sequence_cpp_type.sequence_storage_type));
|
||||
|
||||
// To create an IDL value of type sequence<T> given an iterable iterable and an iterator getter method, perform the following steps:
|
||||
// 1. Let iter be ? GetIterator(iterable, sync, method).
|
||||
// 2. Initialize i to be 0.
|
||||
// 3. Repeat
|
||||
// 1. Let next be ? IteratorStep(iter).
|
||||
// 2. If next is false, then return an IDL sequence value of type sequence<T> of length i, where the value of the element at index j is Sj.
|
||||
// 3. Let nextItem be ? IteratorValue(next).
|
||||
// 4. Initialize Si to the result of converting nextItem to an IDL value of type T.
|
||||
// 5. Set i to i + 1.
|
||||
|
||||
sequence_generator.append(R"~~~(
|
||||
auto iterator@recursion_depth@ = TRY(JS::get_iterator(global_object, @iterable_cpp_name@, JS::IteratorHint::Sync, @iterator_method_cpp_name@));
|
||||
)~~~");
|
||||
|
||||
if (sequence_cpp_type.sequence_storage_type == SequenceStorageType::Vector) {
|
||||
sequence_generator.append(R"~~~(
|
||||
@sequence.storage_type@<@sequence.type@> @cpp_name@;
|
||||
)~~~");
|
||||
} else {
|
||||
sequence_generator.append(R"~~~(
|
||||
@sequence.storage_type@ @cpp_name@ { global_object.heap() };
|
||||
)~~~");
|
||||
}
|
||||
|
||||
sequence_generator.append(R"~~~(
|
||||
for (;;) {
|
||||
auto* next@recursion_depth@ = TRY(JS::iterator_step(global_object, iterator@recursion_depth@));
|
||||
if (!next@recursion_depth@)
|
||||
break;
|
||||
|
||||
auto next_item@recursion_depth@ = TRY(JS::iterator_value(global_object, *next@recursion_depth@));
|
||||
)~~~");
|
||||
|
||||
// FIXME: Sequences types should be TypeWithExtendedAttributes, which would allow us to get [LegacyNullToEmptyString] here.
|
||||
IDL::Parameter parameter { .type = parameters.first(), .name = iterable_cpp_name, .optional_default_value = {}, .extended_attributes = {} };
|
||||
generate_to_cpp(sequence_generator, parameter, "next_item", String::number(recursion_depth), String::formatted("sequence_item{}", recursion_depth), dictionaries, false, false, {}, false, recursion_depth);
|
||||
|
||||
sequence_generator.append(R"~~~(
|
||||
@cpp_name@.append(sequence_item@recursion_depth@);
|
||||
}
|
||||
)~~~");
|
||||
}
|
||||
|
||||
enum class WrappingReference {
|
||||
No,
|
||||
Yes,
|
||||
|
@ -2500,6 +2652,7 @@ void generate_constructor_implementation(IDL::Interface const& interface)
|
|||
generator.append(R"~~~(
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/IteratorOperations.h>
|
||||
#include <LibWeb/Bindings/@constructor_class@.h>
|
||||
#include <LibWeb/Bindings/@prototype_class@.h>
|
||||
#include <LibWeb/Bindings/@wrapper_class@.h>
|
||||
|
@ -2774,6 +2927,7 @@ void generate_prototype_implementation(IDL::Interface const& interface)
|
|||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/FunctionObject.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/IteratorOperations.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibWeb/Bindings/@prototype_class@.h>
|
||||
#include <LibWeb/Bindings/@wrapper_class@.h>
|
||||
|
|
Loading…
Reference in a new issue