Libraries: Remove LibIDL

Move the overload-resolution metadata types into LibWeb::WebIDL and
update the Python generator and overload resolver to use them.

LibIDL no longer has any users after the C++ bindings generator removal,
so remove the library and unlink it from LibWeb.
This commit is contained in:
Shannon Booth 2026-06-06 20:17:00 +02:00 committed by Andreas Kling
parent efe144552c
commit 4a681c9148
15 changed files with 322 additions and 2678 deletions

View file

@ -1,6 +1,5 @@
add_subdirectory(LibCore)
add_subdirectory(LibFileSystem)
add_subdirectory(LibIDL)
add_subdirectory(LibMain)
add_subdirectory(LibRegex)
add_subdirectory(LibSync)

View file

@ -1,8 +0,0 @@
set(SOURCES
ExposedTo.cpp
IDLParser.cpp
Types.cpp
)
ladybird_lib(LibIDL idl)
target_link_libraries(LibIDL PRIVATE LibCore LibFileSystem)

View file

@ -1,77 +0,0 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteString.h>
#include <AK/NeverDestroyed.h>
#include <AK/Vector.h>
#include <LibIDL/ExposedTo.h>
static auto& error_string()
{
static NeverDestroyed<ByteString> string;
return *string;
}
namespace IDL {
ErrorOr<ExposedTo> parse_exposure_set(StringView interface_name, StringView exposed)
{
// NOTE: This roughly follows the definitions of https://webidl.spec.whatwg.org/#Exposed
// It does not remotely interpret all the abstract operations therein though.
auto exposed_trimmed = exposed.trim_whitespace();
if (exposed_trimmed == "*"sv)
return ExposedTo::All;
if (exposed_trimmed == "Nobody"sv)
return ExposedTo::Nobody;
auto exposed_from_string = [](auto& string) -> Optional<ExposedTo> {
if (string == "Window"sv)
return ExposedTo::Window;
if (string == "Worker"sv)
return ExposedTo::AllWorkers;
if (string == "DedicatedWorker"sv)
return ExposedTo::DedicatedWorker;
if (string == "SharedWorker"sv)
return ExposedTo::SharedWorker;
if (string == "ServiceWorker"sv)
return ExposedTo::ServiceWorker;
if (string == "AudioWorklet"sv)
return ExposedTo::AudioWorklet;
if (string == "LayoutWorklet"sv)
return ExposedTo::LayoutWorklet;
if (string == "PaintWorklet"sv)
return ExposedTo::PaintWorklet;
if (string == "Worklet"sv)
return ExposedTo::Worklet;
return {};
};
if (auto parsed_exposed = exposed_from_string(exposed_trimmed); parsed_exposed.has_value())
return parsed_exposed.value();
if (exposed_trimmed[0] == '(') {
ExposedTo whom = ExposedTo::Nobody;
for (StringView candidate : exposed_trimmed.substring_view(1, exposed_trimmed.length() - 1).split_view(',')) {
candidate = candidate.trim_whitespace();
if (auto parsed_exposed = exposed_from_string(candidate); parsed_exposed.has_value()) {
whom |= parsed_exposed.value();
} else {
error_string() = ByteString::formatted("Unknown Exposed attribute candidate {} in {} in {}", candidate, exposed_trimmed, interface_name);
return Error::from_string_view(error_string().view());
}
}
if (whom == ExposedTo::Nobody) {
error_string() = ByteString::formatted("Unknown Exposed attribute {} in {}", exposed_trimmed, interface_name);
return Error::from_string_view(error_string().view());
}
return whom;
}
error_string() = ByteString::formatted("Unknown Exposed attribute {} in {}", exposed_trimmed, interface_name);
return Error::from_string_view(error_string().view());
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/EnumBits.h>
#include <AK/Error.h>
#include <AK/StringView.h>
namespace IDL {
enum class ExposedTo {
Nobody = 0x0,
DedicatedWorker = 0x1,
SharedWorker = 0x2,
ServiceWorker = 0x4,
AudioWorklet = 0x8,
Window = 0x10,
Worklet = 0x40,
PaintWorklet = 0x80,
LayoutWorklet = 0x100,
// FIXME: Categorize PaintWorklet and LayoutWorklet once we have them and know what they are.
AllWorkers = DedicatedWorker | SharedWorker | ServiceWorker | AudioWorklet, // FIXME: Is "AudioWorklet" a Worker? We'll assume it is for now (here, and line below)
All = AllWorkers | Window | Worklet,
};
AK_ENUM_BITWISE_OPERATORS(ExposedTo);
ErrorOr<ExposedTo> parse_exposure_set(StringView interface_name, StringView exposed);
}

File diff suppressed because it is too large Load diff

View file

@ -1,79 +0,0 @@
/*
* Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/GenericLexer.h>
#include <LibIDL/Types.h>
namespace IDL {
class Parser {
public:
static Module parse(ByteString filename, StringView contents, Context& context);
private:
// https://webidl.spec.whatwg.org/#dfn-special-operation
// A special operation is a getter, setter or deleter.
enum class IsSpecialOperation {
No,
Yes,
};
enum class IsStatic {
No,
Yes,
};
Parser(ByteString filename, StringView contents, Context& context);
Module& parse();
void assert_specific(char ch);
void assert_string(StringView expected);
void consume_whitespace();
HashMap<ByteString, ByteString> parse_extended_attributes();
void parse_attribute(HashMap<ByteString, ByteString>& extended_attributes, Interface&, IsStatic is_static = IsStatic::No);
void parse_interface(Interface&);
void parse_partial_interface(HashMap<ByteString, ByteString> extended_attributes, Interface& parent);
void parse_namespace(Interface&);
void parse_partial_namespace(Interface& parent);
void parse_non_interface_entities(bool allow_interface, Interface&);
void parse_enumeration(HashMap<ByteString, ByteString>, Interface&);
void parse_typedef(Interface&);
void parse_callback_interface(HashMap<ByteString, ByteString> extended_attributes, Interface&);
void parse_interface_mixin(Interface&);
void parse_partial_interface_mixin(Interface&);
void parse_dictionary(HashMap<ByteString, ByteString> extended_attributes, Interface&);
void parse_callback_function(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_constructor(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_getter(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_setter(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_deleter(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_stringifier(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
void parse_iterable(Interface&);
void parse_async_iterable(Interface&);
void parse_setlike(Interface&, bool is_readonly);
void parse_maplike(Interface&, bool is_readonly);
Function parse_function(HashMap<ByteString, ByteString>& extended_attributes, Interface&, IsStatic is_static = IsStatic::No, IsSpecialOperation is_special_operation = IsSpecialOperation::No);
Vector<Parameter> parse_parameters();
NonnullRefPtr<Type const> parse_type();
void parse_constant(Interface&);
ByteString parse_identifier_until(AK::Function<bool(char)> predicate);
ByteString parse_identifier_ending_with(auto... possible_terminating_characters);
ByteString parse_identifier_ending_with_space();
ByteString parse_identifier_ending_with_space_or(auto... possible_terminating_characters);
ByteString filename;
StringView input;
LineTrackingLexer lexer;
Context& context;
};
}

View file

@ -1,446 +0,0 @@
/*
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <LibIDL/Types.h>
namespace IDL {
Interface& Context::add_interface(NonnullOwnPtr<Interface> interface)
{
auto& interface_ref = *interface;
if (!interface_ref.name.is_empty())
interfaces.set(interface_ref.name, &interface_ref);
owned_interfaces.append(move(interface));
return interface_ref;
}
Interface& Context::add_mixin(NonnullOwnPtr<Interface> interface)
{
auto& interface_ref = *interface;
mixins.set(interface_ref.name, &interface_ref);
for (auto& partial_mixin : partial_mixins) {
if (partial_mixin->name == interface_ref.name)
interface_ref.extend_with_partial_interface(*partial_mixin);
}
owned_mixins.append(move(interface));
return interface_ref;
}
Module& Context::add_module(NonnullOwnPtr<Module> module)
{
auto& module_ref = *module;
owned_modules.append(move(module));
return module_ref;
}
Module* Context::find_parsed_module(ByteString const& module_path)
{
for (auto const& module : owned_modules) {
if (module->module_own_path == module_path)
return module.ptr();
}
return nullptr;
}
ParameterizedType const& Type::as_parameterized() const
{
return as<ParameterizedType const>(*this);
}
ParameterizedType& Type::as_parameterized()
{
return as<ParameterizedType>(*this);
}
UnionType const& Type::as_union() const
{
return as<UnionType const>(*this);
}
UnionType& Type::as_union()
{
return as<UnionType>(*this);
}
NonnullRefPtr<Type const> clone_type(Type const& type, bool nullable)
{
if (is<ParameterizedType>(type)) {
Vector<NonnullRefPtr<Type const>> parameters;
for (auto& parameter : type.as_parameterized().parameters())
parameters.append(clone_type(parameter, parameter->is_nullable()));
return adopt_ref(*new ParameterizedType(type.name(), nullable, move(parameters)));
}
if (is<UnionType>(type)) {
Vector<NonnullRefPtr<Type const>> member_types;
for (auto& member_type : type.as_union().member_types())
member_types.append(clone_type(member_type, member_type->is_nullable()));
return adopt_ref(*new UnionType(type.name(), nullable, move(member_types)));
}
return adopt_ref(*new Type(type.name(), nullable));
}
// https://webidl.spec.whatwg.org/#dfn-includes-a-nullable-type
bool Type::includes_nullable_type() const
{
// A type includes a nullable type if:
// - the type is a nullable type, or
if (is_nullable())
return true;
// FIXME: - the type is an annotated type and its inner type is a nullable type, or
// - the type is a union type and its number of nullable member types is 1.
if (is_union() && as_union().number_of_nullable_member_types() == 1)
return true;
return false;
}
// https://webidl.spec.whatwg.org/#dfn-includes-undefined
bool Type::includes_undefined() const
{
// A type includes undefined if:
// - the type is undefined, or
if (is_undefined())
return true;
// - the type is a nullable type and its inner type includes undefined, or
// NOTE: We don't treat nullable as its own type, so this is handled by the other cases.
// FIXME: - the type is an annotated type and its inner type includes undefined, or
// - the type is a union type and one of its member types includes undefined.
if (is_union())
return as_union().member_types().contains([](auto& type) { return type->includes_undefined(); });
return false;
}
// https://webidl.spec.whatwg.org/#dfn-distinguishable
bool Type::is_distinguishable_from(IDL::Interface const& interface, IDL::Type const& other) const
{
// 1. If one type includes a nullable type and the other type either includes a nullable type,
// is a union type with flattened member types including a dictionary type, or is a dictionary type,
// return false.
if (includes_nullable_type() && (other.includes_nullable_type() || (other.is_union() && any_of(other.as_union().flattened_member_types(), [&interface](auto const& type) { return interface.context.dictionaries.contains(type->name()); })) || interface.context.dictionaries.contains(other.name())))
return false;
// 2. If both types are either a union type or nullable union type, return true if each member type
// of the one is distinguishable with each member type of the other, or false otherwise.
if (is_union() && other.is_union()) {
auto const& this_union = as_union();
auto const& other_union = other.as_union();
for (auto& this_member_type : this_union.member_types()) {
for (auto& other_member_type : other_union.member_types()) {
if (!this_member_type->is_distinguishable_from(interface, other_member_type))
return false;
}
}
return true;
}
// 3. If one type is a union type or nullable union type, return true if each member type of the union
// type is distinguishable with the non-union type, or false otherwise.
if (is_union() || other.is_union()) {
auto const& the_union = is_union() ? as_union() : other.as_union();
auto const& non_union = is_union() ? other : *this;
for (auto& member_type : the_union.member_types()) {
if (!non_union.is_distinguishable_from(interface, member_type))
return false;
}
return true;
}
// 4. Consider the two "innermost" types derived by taking each types inner type if it is an annotated type,
// and then taking its inner type inner type if the result is a nullable type. If these two innermost types
// appear or are in categories appearing in the following table and there is a “●” mark in the corresponding
// entry or there is a letter in the corresponding entry and the designated additional requirement below the
// table is satisfied, then return true. Otherwise return false.
auto const& this_innermost_type = innermost_type();
auto const& other_innermost_type = other.innermost_type();
enum class DistinguishabilityCategory {
Undefined,
Boolean,
Numeric,
BigInt,
String,
Object,
Symbol,
InterfaceLike,
CallbackFunction,
DictionaryLike,
SequenceLike,
__Count
};
// See https://webidl.spec.whatwg.org/#distinguishable-table
// clang-format off
static constexpr bool table[to_underlying(DistinguishabilityCategory::__Count)][to_underlying(DistinguishabilityCategory::__Count)] {
{false, true, true, true, true, true, true, true, true, false, true},
{ true, false, true, true, true, true, true, true, true, true, true},
{ true, true, false, true, true, true, true, true, true, true, true},
{ true, true, true, false, true, true, true, true, true, true, true},
{ true, true, true, true, false, true, true, true, true, true, true},
{ true, true, true, true, true, false, true, false, false, false, false},
{ true, true, true, true, true, true, false, true, true, true, true},
{ true, true, true, true, true, false, true, false, true, true, true},
{ true, true, true, true, true, false, true, true, false, false, true},
{false, true, true, true, true, false, true, true, false, false, true},
{ true, true, true, true, true, false, true, true, true, true, false},
};
// clang-format on
auto determine_category = [&interface](Type const& type) -> DistinguishabilityCategory {
if (type.is_undefined())
return DistinguishabilityCategory::Undefined;
if (type.is_boolean())
return DistinguishabilityCategory::Boolean;
if (type.is_numeric())
return DistinguishabilityCategory::Numeric;
if (type.is_bigint())
return DistinguishabilityCategory::BigInt;
if (type.is_string())
return DistinguishabilityCategory::String;
if (type.is_object())
return DistinguishabilityCategory::Object;
if (type.is_symbol())
return DistinguishabilityCategory::Symbol;
// FIXME: InterfaceLike - see below
// FIXME: CallbackFunction
// DictionaryLike
// * Dictionary Types
// * Record Types
// FIXME: * Callback Interface Types
if (interface.context.dictionaries.contains(type.name()) || (type.is_parameterized() && type.name() == "record"sv))
return DistinguishabilityCategory::DictionaryLike;
// FIXME: Frozen array types are included in "sequence-like"
if (type.is_sequence())
return DistinguishabilityCategory::SequenceLike;
// FIXME: For lack of a better way of determining if something is an interface type, this just assumes anything we don't recognise is one.
dbgln_if(IDL_DEBUG, "Unable to determine category for type named '{}', assuming it's an interface type.", type.name());
return DistinguishabilityCategory::InterfaceLike;
};
auto this_distinguishability = determine_category(this_innermost_type);
auto other_distinguishability = determine_category(other_innermost_type);
if (this_distinguishability == DistinguishabilityCategory::InterfaceLike && other_distinguishability == DistinguishabilityCategory::InterfaceLike) {
// The two identified interface-like types are not the same, and
// FIXME: no single platform object implements both interface-like types.
return this_innermost_type.name() != other_innermost_type.name();
}
return table[to_underlying(this_distinguishability)][to_underlying(other_distinguishability)];
}
// https://webidl.spec.whatwg.org/#buffer-types
bool Type::is_buffer() const
{
// The buffer types are ArrayBuffer and SharedArrayBuffer.
return m_name.is_one_of("ArrayBuffer", "SharedArrayBuffer");
}
// https://webidl.spec.whatwg.org/#dfn-typed-array-type
bool Type::is_typed_array() const
{
// The typed array types are Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, Uint32Array, Uint8ClampedArray, BigInt64Array, BigUint64Array, Float16Array, Float32Array, and Float64Array.
return m_name.is_one_of("Int8Array", "Int16Array", "Int32Array", "Uint8Array", "Uint16Array", "Uint32Array", "Uint8ClampedArray", "BigInt64Array", "BigUint64Array", "Float16Array", "Float32Array", "Float64Array");
}
// https://webidl.spec.whatwg.org/#buffer-view-types
bool Type::is_buffer_view() const
{
// The buffer view types are DataView and the typed array types.
return m_name == "DataView" || is_typed_array();
}
// https://webidl.spec.whatwg.org/#dfn-buffer-source-type
bool Type::is_buffer_source() const
{
// The buffer source types are the buffer types and the buffer view types.
return is_buffer() || is_buffer_view();
}
// https://webidl.spec.whatwg.org/#dfn-json-types
bool Type::is_json(Context const& context) const
{
// The JSON types are:
// - numeric types,
if (is_numeric())
return true;
// - boolean,
if (is_boolean())
return true;
// - string types,
if (is_string() || context.enumerations.contains(m_name))
return true;
// - object,
if (is_object())
return true;
// - nullable types whose inner type is a JSON type,
// - annotated types whose inner type is a JSON type,
// NOTE: We don't separate nullable and annotated into separate types.
// - union types whose member types are JSON types,
if (is_union()) {
auto const& union_type = as_union();
for (auto const& type : union_type.member_types()) {
if (!type->is_json(context))
return false;
}
return true;
}
// - typedefs whose type being given a new name is a JSON type,
auto typedef_iterator = context.typedefs.find(m_name);
if (typedef_iterator != context.typedefs.end())
return typedef_iterator->value.type->is_json(context);
// - sequence types whose parameterized type is a JSON type,
// - frozen array types whose parameterized type is a JSON type,
// - records where all of their values are JSON types,
if (is_parameterized() && m_name.is_one_of("sequence", "FrozenArray", "record")) {
auto const& parameterized_type = as_parameterized();
for (auto const& parameter : parameterized_type.parameters()) {
if (!parameter->is_json(context))
return false;
}
return true;
}
// - dictionary types where the types of all members declared on the dictionary and all its inherited dictionaries are JSON types,
auto dictionary_iterator = context.dictionaries.find(m_name);
if (dictionary_iterator != context.dictionaries.end()) {
auto const& dictionary = dictionary_iterator->value;
for (auto const& member : dictionary.members) {
if (!member.type->is_json(context))
return false;
}
return true;
}
// - interface types that have a toJSON operation declared on themselves or one of their inherited interfaces.
auto current_interface = context.interfaces.get(m_name);
while (current_interface.has_value()) {
auto to_json_iterator = current_interface.value()->functions.find_if([](IDL::Function const& function) {
return function.name == "toJSON"sv;
});
if (to_json_iterator != current_interface.value()->functions.end())
return true;
if (current_interface.value()->parent_name.is_empty())
break;
current_interface = context.interfaces.get(current_interface.value()->parent_name);
VERIFY(current_interface.has_value());
}
return false;
}
void EffectiveOverloadSet::remove_all_other_entries()
{
Vector<Item> new_items;
new_items.append(m_items[*m_last_matching_item_index]);
m_items = move(new_items);
}
void Interface::dump()
{
dbgln("Attributes:");
for (auto& attribute : attributes) {
dbgln(" {}{}{}{} {}",
attribute.inherit ? "inherit " : "",
attribute.readonly ? "readonly " : "",
attribute.type->name(),
attribute.type->is_nullable() ? "?" : "",
attribute.name);
}
dbgln("Functions:");
for (auto& function : functions) {
dbgln(" {}{} {}",
function.return_type->name(),
function.return_type->is_nullable() ? "?" : "",
function.name);
for (auto& parameter : function.parameters) {
dbgln(" {}{} {}",
parameter.type->name(),
parameter.type->is_nullable() ? "?" : "",
parameter.name);
}
}
dbgln("Static Functions:");
for (auto& function : static_functions) {
dbgln(" static {}{} {}",
function.return_type->name(),
function.return_type->is_nullable() ? "?" : "",
function.name);
for (auto& parameter : function.parameters) {
dbgln(" {}{} {}",
parameter.type->name(),
parameter.type->is_nullable() ? "?" : "",
parameter.name);
}
}
}
void Interface::extend_with_partial_interface(Interface const& partial)
{
for (auto const& attribute : partial.attributes) {
auto attribute_copy = attribute;
attribute_copy.extended_attributes.update(partial.extended_attributes);
attributes.append(move(attribute_copy));
}
for (auto const& static_attribute : partial.static_attributes) {
auto static_attribute_copy = static_attribute;
static_attribute_copy.extended_attributes.update(partial.extended_attributes);
static_attributes.append(move(static_attribute_copy));
}
constants.extend(partial.constants);
for (auto const& function : partial.functions) {
auto function_copy = function;
function_copy.extended_attributes.update(partial.extended_attributes);
functions.append(move(function_copy));
}
for (auto const& static_function : partial.static_functions) {
auto static_function_copy = static_function;
static_function_copy.extended_attributes.update(partial.extended_attributes);
static_functions.append(move(static_function_copy));
}
}
}

View file

@ -1,522 +0,0 @@
/*
* Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/HashMap.h>
#include <AK/HashTable.h>
#include <AK/NonnullRefPtr.h>
#include <AK/SourceGenerator.h>
#include <AK/Tuple.h>
namespace IDL {
template<typename FunctionType>
static size_t get_function_shortest_length(FunctionType& function)
{
size_t length = 0;
for (auto& parameter : function.parameters) {
if (!parameter.optional && !parameter.variadic)
length++;
}
return length;
}
class Context;
class ParameterizedType;
class UnionType;
class Interface;
struct Module;
class Type : public RefCounted<Type> {
public:
enum class Kind {
Plain, // AKA, Type.
Parameterized,
Union,
};
Type(ByteString name, bool nullable)
: m_kind(Kind::Plain)
, m_name(move(name))
, m_nullable(nullable)
{
}
Type(Kind kind, ByteString name, bool nullable)
: m_kind(kind)
, m_name(move(name))
, m_nullable(nullable)
{
}
virtual ~Type() = default;
Kind kind() const { return m_kind; }
bool is_plain() const { return m_kind == Kind::Plain; }
bool is_parameterized() const { return m_kind == Kind::Parameterized; }
ParameterizedType const& as_parameterized() const;
ParameterizedType& as_parameterized();
bool is_union() const { return m_kind == Kind::Union; }
UnionType const& as_union() const;
UnionType& as_union();
ByteString const& name() const { return m_name; }
bool is_nullable() const { return m_nullable; }
void set_nullable(bool value) { m_nullable = value; }
// https://webidl.spec.whatwg.org/#dfn-includes-a-nullable-type
bool includes_nullable_type() const;
// -> https://webidl.spec.whatwg.org/#dfn-includes-undefined
bool includes_undefined() const;
Type const& innermost_type() const
{
// From step 4 of https://webidl.spec.whatwg.org/#dfn-distinguishable
// "Consider the two "innermost" types derived by taking each types inner type if it is an annotated type, and then taking its inner type inner type if the result is a nullable type."
// FIXME: Annotated types.
VERIFY(!is_union());
return *this;
}
// https://webidl.spec.whatwg.org/#idl-any
bool is_any() const { return is_plain() && m_name == "any"; }
// https://webidl.spec.whatwg.org/#idl-undefined
bool is_undefined() const { return is_plain() && m_name == "undefined"; }
// https://webidl.spec.whatwg.org/#idl-boolean
bool is_boolean() const { return is_plain() && m_name == "boolean"; }
// https://webidl.spec.whatwg.org/#idl-bigint
bool is_bigint() const { return is_plain() && m_name == "bigint"; }
// https://webidl.spec.whatwg.org/#idl-object
bool is_object() const { return is_plain() && m_name == "object"; }
// https://webidl.spec.whatwg.org/#idl-symbol
bool is_symbol() const { return is_plain() && m_name == "symbol"; }
bool is_string() const { return is_plain() && m_name.is_one_of("ByteString", "DOMString", "Utf16DOMString", "USVString", "Utf16USVString"); }
// https://webidl.spec.whatwg.org/#dfn-integer-type
bool is_integer() const { return is_plain() && m_name.is_one_of("byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long"); }
// https://webidl.spec.whatwg.org/#dfn-numeric-type
bool is_numeric() const { return is_plain() && (is_integer() || is_floating_point()); }
// https://webidl.spec.whatwg.org/#dfn-primitive-type
bool is_primitive() const { return is_plain() && (is_numeric() || is_boolean() || m_name == "bigint"); }
// https://webidl.spec.whatwg.org/#idl-sequence
bool is_sequence() const { return is_parameterized() && m_name == "sequence"; }
bool is_buffer() const;
bool is_typed_array() const;
bool is_buffer_view() const;
bool is_buffer_source() const;
// https://webidl.spec.whatwg.org/#dfn-distinguishable
bool is_distinguishable_from(Interface const&, Type const& other) const;
bool is_json(Context const&) const;
bool is_restricted_floating_point() const { return m_name.is_one_of("float", "double"); }
bool is_unrestricted_floating_point() const { return m_name.is_one_of("unrestricted float", "unrestricted double"); }
bool is_floating_point() const { return is_restricted_floating_point() || is_unrestricted_floating_point(); }
private:
Kind m_kind;
ByteString m_name;
bool m_nullable { false };
};
struct Parameter {
NonnullRefPtr<Type const> type;
ByteString name;
bool optional { false };
Optional<ByteString> optional_default_value;
HashMap<ByteString, ByteString> extended_attributes;
bool variadic { false };
};
struct Function {
NonnullRefPtr<Type const> return_type;
ByteString name;
Vector<Parameter> parameters;
HashMap<ByteString, ByteString> extended_attributes;
LineTrackingLexer::Position source_position;
size_t overload_index { 0 };
bool is_overloaded { false };
size_t shortest_length() const { return get_function_shortest_length(*this); }
};
struct Constructor {
ByteString name;
Vector<Parameter> parameters;
HashMap<ByteString, ByteString> extended_attributes;
size_t overload_index { 0 };
bool is_overloaded { false };
size_t shortest_length() const { return get_function_shortest_length(*this); }
};
struct Constant {
NonnullRefPtr<Type const> type;
ByteString name;
ByteString value;
};
struct Attribute {
bool inherit { false };
bool readonly { false };
NonnullRefPtr<Type const> type;
ByteString name;
HashMap<ByteString, ByteString> extended_attributes;
// Added for convenience after parsing
ByteString getter_callback_name;
ByteString setter_callback_name;
};
struct DictionaryMember {
bool required { false };
NonnullRefPtr<Type const> type;
ByteString name;
HashMap<ByteString, ByteString> extended_attributes;
Optional<ByteString> default_value;
};
struct Dictionary {
ByteString parent_name;
Vector<DictionaryMember> members;
HashMap<ByteString, ByteString> extended_attributes;
ByteString module_own_path;
bool is_original_definition { true };
};
struct Typedef {
HashMap<ByteString, ByteString> extended_attributes;
NonnullRefPtr<Type const> type;
};
struct Enumeration {
ByteString module_own_path;
OrderedHashTable<ByteString> values;
OrderedHashMap<ByteString, ByteString> translated_cpp_names;
HashMap<ByteString, ByteString> extended_attributes;
ByteString first_member;
bool is_original_definition { true };
};
struct CallbackFunction {
NonnullRefPtr<Type const> return_type;
Vector<Parameter> parameters;
bool is_legacy_treat_non_object_as_null { false };
};
class ParameterizedType : public Type {
public:
ParameterizedType(ByteString name, bool nullable, Vector<NonnullRefPtr<Type const>> parameters)
: Type(Kind::Parameterized, move(name), nullable)
, m_parameters(move(parameters))
{
}
virtual ~ParameterizedType() override = default;
Vector<NonnullRefPtr<Type const>> const& parameters() const { return m_parameters; }
Vector<NonnullRefPtr<Type const>>& parameters() { return m_parameters; }
private:
Vector<NonnullRefPtr<Type const>> m_parameters;
};
static inline size_t get_shortest_function_length(Vector<Function&> const& overload_set)
{
size_t shortest_length = SIZE_MAX;
for (auto const& function : overload_set)
shortest_length = min(function.shortest_length(), shortest_length);
return shortest_length;
}
class Interface {
AK_MAKE_NONCOPYABLE(Interface);
AK_MAKE_NONMOVABLE(Interface);
public:
explicit Interface(Context& context)
: context(context)
{
}
void dump();
ByteString name;
ByteString parent_name;
ByteString namespaced_name;
ByteString implemented_name;
bool is_namespace { false };
bool is_mixin { false };
bool is_callback_interface { false };
bool is_partial { false };
HashMap<ByteString, ByteString> extended_attributes;
Vector<Attribute> attributes;
Vector<Attribute> static_attributes;
Vector<Constant> constants;
Vector<Constructor> constructors;
Vector<Function> functions;
Vector<Function> static_functions;
bool has_stringifier { false };
Optional<HashMap<ByteString, ByteString>> stringifier_extended_attributes;
Optional<Attribute> stringifier_attribute;
bool has_unscopable_member { false };
Optional<NonnullRefPtr<Type const>> value_iterator_type;
Optional<size_t> value_iterator_offset;
Optional<Tuple<NonnullRefPtr<Type const>, NonnullRefPtr<Type const>>> pair_iterator_types;
Optional<size_t> pair_iterator_offset;
Optional<NonnullRefPtr<Type const>> async_value_iterator_type;
Vector<Parameter> async_value_iterator_parameters;
Optional<NonnullRefPtr<Type const>> set_entry_type;
bool is_set_readonly { false };
Optional<NonnullRefPtr<Type const>> map_key_type;
Optional<NonnullRefPtr<Type const>> map_value_type;
bool is_map_readonly { false };
Optional<Function> named_property_getter;
Optional<Function> named_property_setter;
Optional<Function> indexed_property_getter;
Optional<Function> indexed_property_setter;
Optional<Function> named_property_deleter;
Context& context;
OrderedHashTable<ByteString> own_dictionaries;
OrderedHashTable<ByteString> own_enumerations;
// Added for convenience after parsing
ByteString fully_qualified_name;
ByteString constructor_class;
ByteString prototype_class;
ByteString prototype_base_class;
ByteString namespace_class;
ByteString global_mixin_class;
ByteString module_own_path;
OrderedHashMap<ByteString, Vector<Function&>> overload_sets;
OrderedHashMap<ByteString, Vector<Function&>> static_overload_sets;
OrderedHashMap<ByteString, Vector<Constructor&>> constructor_overload_sets;
// https://webidl.spec.whatwg.org/#dfn-support-indexed-properties
bool supports_indexed_properties() const { return indexed_property_getter.has_value(); }
// https://webidl.spec.whatwg.org/#dfn-support-named-properties
bool supports_named_properties() const { return named_property_getter.has_value(); }
// https://webidl.spec.whatwg.org/#dfn-legacy-platform-object
bool is_legacy_platform_object() const { return !extended_attributes.contains("Global") && (supports_indexed_properties() || supports_named_properties()); }
void extend_with_partial_interface(Interface const&);
};
struct Module {
Context* context { nullptr };
ByteString module_own_path;
OrderedHashTable<ByteString> own_dictionaries;
OrderedHashTable<ByteString> own_enumerations;
Optional<Interface&> interface;
};
class UnionType : public Type {
public:
UnionType(ByteString name, bool nullable, Vector<NonnullRefPtr<Type const>> member_types)
: Type(Kind::Union, move(name), nullable)
, m_member_types(move(member_types))
{
}
virtual ~UnionType() override = default;
Vector<NonnullRefPtr<Type const>> const& member_types() const { return m_member_types; }
Vector<NonnullRefPtr<Type const>>& member_types() { return m_member_types; }
// https://webidl.spec.whatwg.org/#dfn-flattened-union-member-types
Vector<NonnullRefPtr<Type const>> flattened_member_types() const
{
// 1. Let T be the union type.
// 2. Initialize S to ∅.
Vector<NonnullRefPtr<Type const>> types;
// 3. For each member type U of T:
for (auto& type : m_member_types) {
// FIXME: 1. If U is an annotated type, then set U to be the inner type of U.
// 2. If U is a nullable type, then set U to be the inner type of U. (NOTE: Not necessary as nullable is stored with Type and not as a separate struct)
// 3. If U is a union type, then add to S the flattened member types of U.
if (type->is_union()) {
auto& union_member_type = type->as_union();
types.extend(union_member_type.flattened_member_types());
} else {
// 4. Otherwise, U is not a union type. Add U to S.
types.append(type);
}
}
// 4. Return S.
return types;
}
// https://webidl.spec.whatwg.org/#dfn-number-of-nullable-member-types
size_t number_of_nullable_member_types() const
{
// 1. Let T be the union type.
// 2. Initialize n to 0.
size_t num_nullable_member_types = 0;
// 3. For each member type U of T:
for (auto& type : m_member_types) {
// 1. If U is a nullable type, then:
if (type->is_nullable()) {
// 1. Set n to n + 1.
++num_nullable_member_types;
// 2. Set U to be the inner type of U. (NOTE: Not necessary as nullable is stored with Type and not as a separate struct)
}
// 2. If U is a union type, then:
if (type->is_union()) {
auto& union_member_type = type->as_union();
// 1. Let m be the number of nullable member types of U.
// 2. Set n to n + m.
num_nullable_member_types += union_member_type.number_of_nullable_member_types();
}
}
// 4. Return n.
return num_nullable_member_types;
}
private:
Vector<NonnullRefPtr<Type const>> m_member_types;
};
NonnullRefPtr<Type const> clone_type(Type const&, bool nullable);
class Context {
public:
Interface& add_interface(NonnullOwnPtr<Interface>);
Interface& add_mixin(NonnullOwnPtr<Interface>);
Module& add_module(NonnullOwnPtr<Module>);
Module* find_parsed_module(ByteString const& module_path);
void resolve();
HashMap<ByteString, Interface*> interfaces;
Vector<NonnullOwnPtr<Interface>> owned_interfaces;
HashMap<ByteString, Dictionary> dictionaries;
HashMap<ByteString, Vector<Dictionary>> partial_dictionaries;
HashMap<ByteString, Enumeration> enumerations;
HashMap<ByteString, Typedef> typedefs;
HashMap<ByteString, CallbackFunction> callback_functions;
HashMap<ByteString, Interface*> mixins;
Vector<NonnullOwnPtr<Interface>> owned_mixins;
Vector<NonnullOwnPtr<Interface>> partial_interfaces;
Vector<NonnullOwnPtr<Interface>> partial_mixins;
Vector<NonnullOwnPtr<Interface>> partial_namespaces;
HashMap<ByteString, HashTable<ByteString>> included_mixins;
Vector<NonnullOwnPtr<Module>> owned_modules;
};
// https://webidl.spec.whatwg.org/#dfn-optionality-value
enum class Optionality {
Required,
Optional,
Variadic,
};
// https://webidl.spec.whatwg.org/#dfn-effective-overload-set
class EffectiveOverloadSet {
public:
struct Item {
int callable_id;
Vector<NonnullRefPtr<Type const>> types;
Vector<Optionality> optionality_values;
};
EffectiveOverloadSet(Vector<Item> items, size_t distinguishing_argument_index)
: m_items(move(items))
, m_distinguishing_argument_index(distinguishing_argument_index)
{
}
Vector<Item>& items() { return m_items; }
Vector<Item> const& items() const { return m_items; }
Item const& only_item() const
{
VERIFY(m_items.size() == 1);
return m_items[0];
}
bool is_empty() const { return m_items.is_empty(); }
size_t size() const { return m_items.size(); }
size_t distinguishing_argument_index() const { return m_distinguishing_argument_index; }
template<typename Matches>
bool has_overload_with_matching_argument_at_index(size_t index, Matches matches)
{
for (size_t i = 0; i < m_items.size(); ++i) {
auto const& item = m_items[i];
if (matches(item.types[index], item.optionality_values[index])) {
m_last_matching_item_index = i;
return true;
}
}
m_last_matching_item_index = {};
return false;
}
void remove_all_other_entries();
private:
// FIXME: This should be an "ordered set".
Vector<Item> m_items;
size_t m_distinguishing_argument_index { 0 };
Optional<size_t> m_last_matching_item_index;
};
}

View file

@ -1179,6 +1179,7 @@ set(SOURCES
WebIDL/DOMException.cpp
WebIDL/ObservableArray.cpp
WebIDL/OverloadResolution.cpp
WebIDL/OverloadTypes.cpp
WebIDL/Promise.cpp
WebIDL/QuotaExceededError.cpp
WebIDL/Tracing.cpp
@ -1254,7 +1255,7 @@ set(GENERATED_SOURCES
ladybird_lib(LibWeb web EXPLICIT_SYMBOL_EXPORT)
target_link_libraries(LibWeb PRIVATE LibCore LibCompress LibCrypto LibJS LibHTTP LibGfx LibIPC LibRegex LibSyntax LibTextCodec LibUnicode LibMedia LibWasm LibXML LibIDL LibURL LibTLS LibRequests LibGC LibSync LibThreading skia ${ANGLE_TARGETS} SDL3::SDL3 LibXml2::LibXml2)
target_link_libraries(LibWeb PRIVATE LibCore LibCompress LibCrypto LibJS LibHTTP LibGfx LibIPC LibRegex LibSyntax LibTextCodec LibUnicode LibMedia LibWasm LibXML LibURL LibTLS LibRequests LibGC LibSync LibThreading skia ${ANGLE_TARGETS} SDL3::SDL3 LibXml2::LibXml2)
import_rust_crate(MANIFEST_PATH Rust/Cargo.toml CRATE_NAME libweb_rust FFI_HEADER RustFFI.h)

View file

@ -16,14 +16,14 @@
namespace Web::WebIDL {
// https://webidl.spec.whatwg.org/#dfn-convert-ecmascript-to-idl-value
static JS::Value convert_ecmascript_type_to_idl_value(JS::Value value, IDL::Type const&)
static JS::Value convert_ecmascript_type_to_idl_value(JS::Value value, Type const&)
{
// FIXME: We have this code already in the code generator, in `generate_to_cpp()`, but how do we use it here?
return value;
}
template<typename Match>
static bool has_overload_with_argument_type_or_subtype_matching(IDL::EffectiveOverloadSet& overloads, size_t argument_index, Match match)
static bool has_overload_with_argument_type_or_subtype_matching(EffectiveOverloadSet& overloads, size_t argument_index, Match match)
{
// NOTE: This is to save some repetition.
// Almost every sub-step of step 12 of the overload resolution algorithm matches overloads with an argument that is:
@ -33,7 +33,7 @@ static bool has_overload_with_argument_type_or_subtype_matching(IDL::EffectiveOv
// So, this function lets you pass in the first check, and handles the others automatically.
return overloads.has_overload_with_matching_argument_at_index(argument_index,
[match](IDL::Type const& type, auto) {
[match](Type const& type, auto) {
if (match(type))
return true;
@ -55,9 +55,9 @@ static bool has_overload_with_argument_type_or_subtype_matching(IDL::EffectiveOv
}
// https://webidl.spec.whatwg.org/#es-overloads
JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::EffectiveOverloadSet& overloads, ReadonlySpan<StringView> dictionary_types)
JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, EffectiveOverloadSet& overloads, ReadonlySpan<StringView> dictionary_types)
{
auto is_dictionary = [&dictionary_types](IDL::Type const& type) {
auto is_dictionary = [&dictionary_types](Type const& type) {
return dictionary_types.contains_slow(type.name());
};
@ -104,7 +104,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
auto const& optionality = item.optionality_values[i];
// 4. If optionality is “optional” and V is undefined, then:
if (optionality == IDL::Optionality::Optional && value.is_undefined()) {
if (optionality == Optionality::Optional && value.is_undefined()) {
// FIXME: 1. If the argument at index i is declared with a default value, then append to values that default value.
// 2. Otherwise, append to values the special value “missing”.
@ -125,7 +125,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// 2. If V is undefined, and there is an entry in S whose list of optionality values has “optional” at index i, then remove from S all other entries.
if (value.is_undefined()
&& overloads.has_overload_with_matching_argument_at_index(i, [](auto&, IDL::Optionality const& optionality) { return optionality == IDL::Optionality::Optional; })) {
&& overloads.has_overload_with_matching_argument_at_index(i, [](auto&, Optionality const& optionality) { return optionality == Optionality::Optional; })) {
overloads.remove_all_other_entries();
}
@ -138,7 +138,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// NOTE: This is the one case we can't use `has_overload_with_argument_type_or_subtype_matching()` because we also need to look
// for dictionary types in the flattened members.
else if ((value.is_undefined() || value.is_null())
&& overloads.has_overload_with_matching_argument_at_index(i, [&is_dictionary](IDL::Type const& type, auto) {
&& overloads.has_overload_with_matching_argument_at_index(i, [&is_dictionary](Type const& type, auto) {
if (type.is_nullable())
return true;
if (is_dictionary(type))
@ -169,7 +169,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_object() && is<Bindings::PlatformObject>(value.as_object())
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [value](IDL::Type const& type) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [value](Type const& type) {
// - an interface type that V implements
if (static_cast<Bindings::PlatformObject const&>(value.as_object()).implements_interface(MUST(String::from_byte_string(type.name()))))
return true;
@ -191,7 +191,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_object() && is<JS::ArrayBuffer>(value.as_object())
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) {
if (type.is_plain() && (type.name() == "ArrayBuffer" || type.name() == "BufferSource"))
return true;
if (type.is_object())
@ -209,7 +209,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_object() && is<JS::DataView>(value.as_object())
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) {
if (type.is_plain() && (type.name() == "DataView" || type.name() == "BufferSource" || type.name() == "ArrayBufferView"))
return true;
if (type.is_object())
@ -227,7 +227,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_object() && value.as_object().is_typed_array()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [&](IDL::Type const& type) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [&](Type const& type) {
if (type.is_plain() && (type.name() == static_cast<JS::TypedArrayBase const&>(value.as_object()).element_name() || type.name() == "BufferSource" || type.name() == "ArrayBufferView"))
return true;
if (type.is_object())
@ -245,7 +245,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_function()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) {
// FIXME: - a callback function type
if (type.is_object())
return true;
@ -266,7 +266,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// }
// method is not undefined, then remove from S all other entries.
else if (value.is_object()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [&vm, &method, &value](IDL::Type const& type) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [&vm, &method, &value](Type const& type) {
// - a sequence type
// FIXME: - a frozen array type
// - a nullable version of any of the above types
@ -297,7 +297,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_object()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [&is_dictionary](IDL::Type const& type) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [&is_dictionary](Type const& type) {
if (is_dictionary(type))
return true;
// FIXME: a callback interface type
@ -314,7 +314,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_boolean()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_boolean(); })) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) { return type.is_boolean(); })) {
overloads.remove_all_other_entries();
}
@ -325,7 +325,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_number()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_numeric(); })) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) { return type.is_numeric(); })) {
overloads.remove_all_other_entries();
}
@ -336,7 +336,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (value.is_bigint()
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_bigint(); })) {
&& has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) { return type.is_bigint(); })) {
overloads.remove_all_other_entries();
}
@ -346,7 +346,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - an annotated type whose inner type is one of the above types
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_string(); })) {
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) { return type.is_string(); })) {
overloads.remove_all_other_entries();
}
@ -356,7 +356,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - an annotated type whose inner type is one of the above types
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_numeric(); })) {
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) { return type.is_numeric(); })) {
overloads.remove_all_other_entries();
}
@ -366,7 +366,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - an annotated type whose inner type is one of the above types
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_boolean(); })) {
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) { return type.is_boolean(); })) {
overloads.remove_all_other_entries();
}
@ -376,7 +376,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
// - an annotated type whose inner type is one of the above types
// - a union type, nullable union type, or annotated union type that has one of the above types in its flattened member types
// then remove from S all other entries.
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](IDL::Type const& type) { return type.is_bigint(); })) {
else if (has_overload_with_argument_type_or_subtype_matching(overloads, i, [](Type const& type) { return type.is_bigint(); })) {
overloads.remove_all_other_entries();
}
@ -427,7 +427,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
auto const& optionality = entry.optionality_values[i];
// 4. If optionality is “optional” and V is undefined, then:
if (optionality == IDL::Optionality::Optional && value.is_undefined()) {
if (optionality == Optionality::Optional && value.is_undefined()) {
// FIXME: 1. If the argument at index i is declared with a default value, then append to values that default value.
// 2. Otherwise, append to values the special value “missing”.
@ -450,7 +450,7 @@ JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM& vm, IDL::Effect
}
// 2. Otherwise, if callables argument at index i is not variadic, then append to values the special value “missing”.
else if (callable.optionality_values[i] != IDL::Optionality::Variadic) {
else if (callable.optionality_values[i] != Optionality::Variadic) {
values.empend(ResolvedOverload::Missing {});
}

View file

@ -9,9 +9,9 @@
#include <AK/Optional.h>
#include <AK/Span.h>
#include <AK/Vector.h>
#include <LibIDL/Types.h>
#include <LibJS/Runtime/VM.h>
#include <LibWeb/Export.h>
#include <LibWeb/WebIDL/OverloadTypes.h>
namespace Web::WebIDL {
@ -25,6 +25,6 @@ struct ResolvedOverload {
};
// https://webidl.spec.whatwg.org/#es-overloads
WEB_API JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM&, IDL::EffectiveOverloadSet&, ReadonlySpan<StringView> interface_dictionaries);
WEB_API JS::ThrowCompletionOr<ResolvedOverload> resolve_overload(JS::VM&, EffectiveOverloadSet&, ReadonlySpan<StringView> interface_dictionaries);
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2026, Ladybird contributors
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibWeb/WebIDL/OverloadTypes.h>
namespace Web::WebIDL {
ParameterizedType const& Type::as_parameterized() const
{
return as<ParameterizedType const>(*this);
}
ParameterizedType& Type::as_parameterized()
{
return as<ParameterizedType>(*this);
}
UnionType const& Type::as_union() const
{
return as<UnionType const>(*this);
}
UnionType& Type::as_union()
{
return as<UnionType>(*this);
}
bool Type::includes_nullable_type() const
{
if (is_nullable())
return true;
if (is_union() && as_union().number_of_nullable_member_types() == 1)
return true;
return false;
}
bool Type::includes_undefined() const
{
if (is_undefined())
return true;
if (is_union())
return as_union().member_types().contains([](auto& type) { return type->includes_undefined(); });
return false;
}
bool Type::is_buffer() const
{
return m_name.is_one_of("ArrayBuffer", "SharedArrayBuffer");
}
bool Type::is_typed_array() const
{
return m_name.is_one_of("Int8Array", "Int16Array", "Int32Array", "Uint8Array", "Uint16Array", "Uint32Array", "Uint8ClampedArray", "BigInt64Array", "BigUint64Array", "Float16Array", "Float32Array", "Float64Array");
}
bool Type::is_buffer_view() const
{
return m_name == "DataView" || is_typed_array();
}
bool Type::is_buffer_source() const
{
return is_buffer() || is_buffer_view();
}
Vector<NonnullRefPtr<Type const>> UnionType::flattened_member_types() const
{
Vector<NonnullRefPtr<Type const>> types;
for (auto& type : m_member_types) {
if (type->is_union())
types.extend(type->as_union().flattened_member_types());
else
types.append(type);
}
return types;
}
size_t UnionType::number_of_nullable_member_types() const
{
size_t num_nullable_member_types = 0;
for (auto& type : m_member_types) {
if (type->is_nullable())
++num_nullable_member_types;
if (type->is_union())
num_nullable_member_types += type->as_union().number_of_nullable_member_types();
}
return num_nullable_member_types;
}
void EffectiveOverloadSet::remove_all_other_entries()
{
Vector<Item> new_items;
new_items.append(m_items[*m_last_matching_item_index]);
m_items = move(new_items);
}
}

View file

@ -0,0 +1,178 @@
/*
* Copyright (c) 2026, Ladybird contributors
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteString.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/RefCounted.h>
#include <AK/Vector.h>
namespace Web::WebIDL {
class ParameterizedType;
class UnionType;
class Type : public RefCounted<Type> {
public:
enum class Kind {
Plain,
Parameterized,
Union,
};
Type(ByteString name, bool nullable)
: m_kind(Kind::Plain)
, m_name(move(name))
, m_nullable(nullable)
{
}
Type(Kind kind, ByteString name, bool nullable)
: m_kind(kind)
, m_name(move(name))
, m_nullable(nullable)
{
}
virtual ~Type() = default;
bool is_plain() const { return m_kind == Kind::Plain; }
bool is_parameterized() const { return m_kind == Kind::Parameterized; }
ParameterizedType const& as_parameterized() const;
ParameterizedType& as_parameterized();
bool is_union() const { return m_kind == Kind::Union; }
UnionType const& as_union() const;
UnionType& as_union();
ByteString const& name() const { return m_name; }
bool is_nullable() const { return m_nullable; }
bool includes_nullable_type() const;
bool includes_undefined() const;
bool is_any() const { return is_plain() && m_name == "any"; }
bool is_undefined() const { return is_plain() && m_name == "undefined"; }
bool is_boolean() const { return is_plain() && m_name == "boolean"; }
bool is_bigint() const { return is_plain() && m_name == "bigint"; }
bool is_object() const { return is_plain() && m_name == "object"; }
bool is_symbol() const { return is_plain() && m_name == "symbol"; }
bool is_string() const { return is_plain() && m_name.is_one_of("ByteString", "DOMString", "Utf16DOMString", "USVString", "Utf16USVString"); }
bool is_integer() const { return is_plain() && m_name.is_one_of("byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long"); }
bool is_numeric() const { return is_plain() && (is_integer() || is_floating_point()); }
bool is_sequence() const { return is_parameterized() && m_name == "sequence"; }
bool is_buffer() const;
bool is_typed_array() const;
bool is_buffer_view() const;
bool is_buffer_source() const;
bool is_restricted_floating_point() const { return m_name.is_one_of("float", "double"); }
bool is_unrestricted_floating_point() const { return m_name.is_one_of("unrestricted float", "unrestricted double"); }
bool is_floating_point() const { return is_restricted_floating_point() || is_unrestricted_floating_point(); }
private:
Kind m_kind;
ByteString m_name;
bool m_nullable { false };
};
class ParameterizedType : public Type {
public:
ParameterizedType(ByteString name, bool nullable, Vector<NonnullRefPtr<Type const>> parameters)
: Type(Kind::Parameterized, move(name), nullable)
, m_parameters(move(parameters))
{
}
virtual ~ParameterizedType() override = default;
Vector<NonnullRefPtr<Type const>> const& parameters() const { return m_parameters; }
private:
Vector<NonnullRefPtr<Type const>> m_parameters;
};
class UnionType : public Type {
public:
UnionType(ByteString name, bool nullable, Vector<NonnullRefPtr<Type const>> member_types)
: Type(Kind::Union, move(name), nullable)
, m_member_types(move(member_types))
{
}
virtual ~UnionType() override = default;
Vector<NonnullRefPtr<Type const>> const& member_types() const { return m_member_types; }
Vector<NonnullRefPtr<Type const>> flattened_member_types() const;
size_t number_of_nullable_member_types() const;
private:
Vector<NonnullRefPtr<Type const>> m_member_types;
};
enum class Optionality {
Required,
Optional,
Variadic,
};
class EffectiveOverloadSet {
public:
struct Item {
int callable_id;
Vector<NonnullRefPtr<Type const>> types;
Vector<Optionality> optionality_values;
};
EffectiveOverloadSet(Vector<Item> items, size_t distinguishing_argument_index)
: m_items(move(items))
, m_distinguishing_argument_index(distinguishing_argument_index)
{
}
Vector<Item>& items() { return m_items; }
Vector<Item> const& items() const { return m_items; }
Item const& only_item() const
{
VERIFY(m_items.size() == 1);
return m_items[0];
}
bool is_empty() const { return m_items.is_empty(); }
size_t size() const { return m_items.size(); }
size_t distinguishing_argument_index() const { return m_distinguishing_argument_index; }
template<typename Matches>
bool has_overload_with_matching_argument_at_index(size_t index, Matches matches)
{
for (size_t i = 0; i < m_items.size(); ++i) {
auto const& item = m_items[i];
if (matches(item.types[index], item.optionality_values[index])) {
m_last_matching_item_index = i;
return true;
}
}
m_last_matching_item_index = {};
return false;
}
void remove_all_other_entries();
private:
Vector<Item> m_items;
size_t m_distinguishing_argument_index { 0 };
Optional<size_t> m_last_matching_item_index;
};
}

View file

@ -44,7 +44,6 @@ def write_constructor_overload_arbiter(
) -> None:
includes.add("AK/Optional.h")
includes.add("AK/Vector.h")
includes.add("LibIDL/Types.h")
includes.add("LibWeb/WebIDL/OverloadResolution.h")
out.write(

View file

@ -51,7 +51,7 @@ def write_overload_resolution_switch(
dictionary_types: set[str] = set()
out.write(
f""" Optional<int> chosen_overload_callable_id;
Optional<IDL::EffectiveOverloadSet> effective_overload_set;
Optional<WebIDL::EffectiveOverloadSet> effective_overload_set;
switch (min({maximum_argument_count}, vm.argument_count())) {{
"""
@ -77,7 +77,7 @@ def write_overload_resolution_switch(
)
out.write(
f""" case {argument_count}: {{
Vector<IDL::EffectiveOverloadSet::Item> overloads;
Vector<WebIDL::EffectiveOverloadSet::Item> overloads;
overloads.ensure_capacity({len(effective_overload_set)});
"""
)
@ -85,10 +85,10 @@ def write_overload_resolution_switch(
dictionary_types.update(context.dictionary_type_names(*overload.types))
types = ", ".join(constructor_for_idl_type(idl_type, context) for idl_type in overload.types)
optionality_values = ", ".join(
f"IDL::Optionality::{optionality.value}" for optionality in overload.optionality_values
f"WebIDL::Optionality::{optionality.value}" for optionality in overload.optionality_values
)
out.write(
f""" overloads.empend({overload.callable_id}, Vector<NonnullRefPtr<IDL::Type const>> {{ {types} }}, Vector<IDL::Optionality> {{ {optionality_values} }});
f""" overloads.empend({overload.callable_id}, Vector<NonnullRefPtr<WebIDL::Type const>> {{ {types} }}, Vector<WebIDL::Optionality> {{ {optionality_values} }});
"""
)
out.write(
@ -130,7 +130,7 @@ def write_overload_arbiter(
includes.add("AK/Optional.h")
includes.add("AK/Vector.h")
includes.add("LibIDL/Types.h")
includes.add("LibWeb/WebIDL/OverloadTypes.h")
includes.add("LibWeb/WebIDL/OverloadResolution.h")
includes.add("LibWeb/WebIDL/Tracing.h")
@ -284,18 +284,18 @@ def constructor_for_idl_type(idl_type: IDLType, context: GenerationContext) -> s
if isinstance(idl_type, IDLParameterizedType):
parameters = ", ".join(constructor_for_idl_type(parameter, context) for parameter in idl_type.parameters)
return (
f'make_ref_counted<IDL::ParameterizedType>("{idl_type.name}", {nullable}, '
f"Vector<NonnullRefPtr<IDL::Type const>> {{ {parameters} }})"
f'make_ref_counted<WebIDL::ParameterizedType>("{idl_type.name}", {nullable}, '
f"Vector<NonnullRefPtr<WebIDL::Type const>> {{ {parameters} }})"
)
if isinstance(idl_type, IDLUnionType):
member_types = ", ".join(
constructor_for_idl_type(member_type, context) for member_type in idl_type.member_types
)
return (
f'make_ref_counted<IDL::UnionType>("{idl_type.name}", {nullable}, '
f"Vector<NonnullRefPtr<IDL::Type const>> {{ {member_types} }})"
f'make_ref_counted<WebIDL::UnionType>("{idl_type.name}", {nullable}, '
f"Vector<NonnullRefPtr<WebIDL::Type const>> {{ {member_types} }})"
)
return f'make_ref_counted<IDL::Type>("{idl_type.name}", {nullable})'
return f'make_ref_counted<WebIDL::Type>("{idl_type.name}", {nullable})'
# https://webidl.spec.whatwg.org/#dfn-distinguishable