ladybird/Meta/Generators/libweb_bindings/iterables.py
Andreas Kling b6bef6b688 Libraries: Use UTF-16 for JS-visible runtime strings
Produce JS-visible string results as UTF-16 at their source, including
numeric formatting, BigInt and BigFraction formatting, URI encoding,
console formatting, parser errors, regular expression errors, Intl and
Temporal records, LibUnicode locale boundaries, and LibWeb bindings.

Handle fractional radix formatting through the UTF-16 builder view.
2026-06-22 19:51:25 +02:00

870 lines
36 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) 2026-present, the Ladybird developers.
#
# SPDX-License-Identifier: BSD-2-Clause
from typing import TextIO
from Generators.libweb_bindings.arguments import write_operation_parameter_conversions
from Generators.libweb_bindings.context import GenerationContext
from Generators.libweb_bindings.cpp_types import add_header_includes_for_idl_type
from Generators.libweb_bindings.cpp_types import fully_qualified_name_for_interface
from Generators.libweb_bindings.cpp_types import idl_identifier_cpp_name
from Generators.libweb_bindings.cpp_types import libweb_include_path
from Generators.libweb_bindings.includes import GeneratedIncludes
from Generators.libweb_bindings.overload_resolution import operation_callback_names
from Generators.libweb_bindings.overload_resolution import parameter_list_length
from Generators.libweb_bindings.to_idl_value import type_check_idl_value
from Generators.libweb_bindings.to_js_value import to_javascript_value
from Utils.utils import make_name_acceptable_cpp
from Utils.utils import title_case_to_snake_case
from Utils.webidl_parser import Interface
def iterator_implementation_header_for_interface(interface: Interface) -> str:
return libweb_include_path(interface.path.with_name(f"{interface.implemented_name}Iterator.h"))
def async_iterator_implementation_header_for_interface(interface: Interface) -> str:
return libweb_include_path(interface.path.with_name(f"{interface.implemented_name}AsyncIterator.h"))
def write_iterator_prototype_declaration(out: TextIO, interface: Interface) -> None:
if interface.iterable is None or interface.iterable.key_type is None:
return
out.write(
f"""class {interface.name}IteratorPrototype : public JS::Object {{
JS_OBJECT({interface.name}IteratorPrototype, JS::Object);
GC_DECLARE_ALLOCATOR({interface.name}IteratorPrototype);
public:
explicit {interface.name}IteratorPrototype(JS::Realm&);
virtual void initialize(JS::Realm&) override;
virtual ~{interface.name}IteratorPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(next);
}};
"""
)
def write_async_iterator_prototype_declaration(out: TextIO, interface: Interface) -> None:
if interface.async_iterable is None:
return
out.write(
f"""class {interface.name}AsyncIteratorPrototype : public JS::Object {{
JS_OBJECT({interface.name}AsyncIteratorPrototype, JS::Object);
GC_DECLARE_ALLOCATOR({interface.name}AsyncIteratorPrototype);
public:
explicit {interface.name}AsyncIteratorPrototype(JS::Realm&);
virtual void initialize(JS::Realm&) override;
virtual ~{interface.name}AsyncIteratorPrototype() override;
private:
JS_DECLARE_NATIVE_FUNCTION(next);
{"JS_DECLARE_NATIVE_FUNCTION(return_);" if "DefinesAsyncIteratorReturn" in interface.extended_attributes else ""}
}};
"""
)
def define_the_pair_iterable_declaration(
out: TextIO,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.iterable is None or interface.iterable.key_type is None:
return
includes.add("LibJS/Runtime/ArrayPrototype.h")
out.write(
""" object.define_native_function(realm, vm.names.entries, entries, 0, default_attributes);
object.define_native_function(realm, vm.names.forEach, for_each, 1, default_attributes);
object.define_native_function(realm, vm.names.keys, keys, 0, default_attributes);
object.define_native_function(realm, vm.names.values, values, 0, default_attributes);
object.define_direct_property(vm.well_known_symbol_iterator(), object.get_without_side_effects(vm.names.entries), JS::Attribute::Configurable | JS::Attribute::Writable);
"""
)
def define_the_async_iterable_declaration(
out: TextIO,
interface: Interface,
) -> None:
if interface.async_iterable is None:
return
out.write(
f""" object.define_native_function(realm, vm.names.values, values, {parameter_list_length(interface.async_iterable.parameters)}, default_attributes);
object.define_direct_property(vm.well_known_symbol_async_iterator(), object.get_without_side_effects(vm.names.values), JS::Attribute::Configurable | JS::Attribute::Writable);
"""
)
def define_the_maplike_declaration(
out: TextIO,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.maplike is None:
return
includes.add("LibJS/Runtime/Map.h")
out.write(
""" object.define_native_accessor(realm, vm.names.size, get_size, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable);
object.define_native_function(realm, vm.names.entries, entries, 0, default_attributes);
object.define_direct_property(vm.well_known_symbol_iterator(), object.get_without_side_effects(vm.names.entries), JS::Attribute::Configurable | JS::Attribute::Writable);
object.define_native_function(realm, vm.names.keys, keys, 0, default_attributes);
object.define_native_function(realm, vm.names.values, values, 0, default_attributes);
object.define_native_function(realm, vm.names.forEach, for_each, 1, default_attributes);
object.define_native_function(realm, vm.names.get, get, 1, default_attributes);
object.define_native_function(realm, vm.names.has, has, 1, default_attributes);
"""
)
if not interface.maplike.readonly:
out.write(
""" object.define_native_function(realm, vm.names.delete_, delete_, 1, default_attributes);
object.define_native_function(realm, vm.names.clear, clear, 0, default_attributes);
"""
)
out.write("\n")
def define_the_setlike_declaration(
out: TextIO,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.setlike is None:
return
includes.add("LibJS/Runtime/Set.h")
operation_callbacks = operation_callback_names(interface)
out.write(
""" object.define_native_accessor(realm, vm.names.size, get_size, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable);
object.define_native_function(realm, vm.names.entries, entries, 0, default_attributes);
object.define_native_function(realm, vm.names.keys, values, 0, default_attributes);
object.define_native_function(realm, vm.names.values, values, 0, default_attributes);
object.define_direct_property(vm.well_known_symbol_iterator(), object.get_without_side_effects(vm.names.values), JS::Attribute::Configurable | JS::Attribute::Writable);
object.define_native_function(realm, vm.names.forEach, for_each, 1, default_attributes);
object.define_native_function(realm, vm.names.has, has, 1, default_attributes);
"""
)
if not interface.setlike.readonly:
if "add" not in operation_callbacks:
out.write(" object.define_native_function(realm, vm.names.add, add, 1, default_attributes);\n")
if "delete_" not in operation_callbacks:
out.write(" object.define_native_function(realm, vm.names.delete_, delete_, 1, default_attributes);\n")
if "clear" not in operation_callbacks:
out.write(" object.define_native_function(realm, vm.names.clear, clear, 0, default_attributes);\n")
out.write("\n")
# https://webidl.spec.whatwg.org/#js-iterable
def write_pair_iterable_declaration_functions(
out: TextIO,
context: GenerationContext,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.iterable is None or interface.iterable.key_type is None:
return
includes.add("LibJS/Runtime/AbstractOperations.h")
includes.add("LibJS/Runtime/Error.h")
includes.add("LibJS/Runtime/ValueInlines.h")
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
includes.add(iterator_implementation_header_for_interface(interface))
add_header_includes_for_idl_type(interface.iterable.key_type, includes, context)
add_header_includes_for_idl_type(interface.iterable.value_type, includes, context)
out.write(f"""JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::entries)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::entries");
// 1. Let jsValue be ? ToObject(this value).
// 2. If jsValue is a platform object, then perform a security check, passing jsValue, "%Symbol.iterator%", and "method".
// 3. If jsValue does not implement definition, then throw a TypeError.
auto* this_impl = TRY(impl_from(vm));
// 4. Return a newly created default iterator object for definition, with jsValue as its target, "key+value" as its kind, and index set to 0.
return TRY(throw_dom_exception_if_needed(vm, [&] {{ return {fully_qualified_name_for_interface(interface)}Iterator::create(*this_impl, JS::Object::PropertyKind::KeyAndValue); }}));
}}
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::keys)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::keys");
// 1. Let jsValue be ? ToObject(this value).
// 2. If jsValue is a platform object, then perform a security check, passing jsValue, "keys", and "method".
// 3. If jsValue does not implement definition, then throw a TypeError.
auto* this_impl = TRY(impl_from(vm));
// 4. Return a newly created default iterator object for definition, with jsValue as its target, "key" as its kind, and index set to 0.
return TRY(throw_dom_exception_if_needed(vm, [&] {{ return {fully_qualified_name_for_interface(interface)}Iterator::create(*this_impl, JS::Object::PropertyKind::Key); }}));
}}
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::values)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::values");
// 1. Let jsValue be ? ToObject(this value).
// 2. If jsValue is a platform object, then perform a security check, passing jsValue, "values", and "method".
// 3. If jsValue does not implement definition, then throw a TypeError.
auto* this_impl = TRY(impl_from(vm));
// 4. Return a newly created default iterator object for definition, with jsValue as its target, "value" as its kind, and index set to 0.
return TRY(throw_dom_exception_if_needed(vm, [&] {{ return {fully_qualified_name_for_interface(interface)}Iterator::create(*this_impl, JS::Object::PropertyKind::Value); }}));
}}
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::for_each)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::for_each");
auto* this_impl = TRY(impl_from(vm));
auto callback = vm.argument(0);
if (!callback.is_function())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, callback);
auto this_value = vm.this_value();
TRY(this_impl->for_each([&](auto key, auto value) -> JS::ThrowCompletionOr<void> {{
JS::Value wrapped_key = {to_javascript_value(interface.iterable.key_type, "key", includes, context)};
JS::Value wrapped_value = {to_javascript_value(interface.iterable.value_type, "value", includes, context)};
TRY(JS::call(vm, callback.as_function(), vm.argument(1), wrapped_value, wrapped_key, this_value));
return {{}};
}}));
return JS::js_undefined();
}}
""")
def write_iterator_prototype_implementation(
out: TextIO,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.iterable is None or interface.iterable.key_type is None:
return
includes.add("AK/TypeCasts.h")
includes.add("LibJS/Runtime/Error.h")
includes.add("LibJS/Runtime/IteratorPrototype.h")
includes.add("LibJS/Runtime/PrimitiveString.h")
includes.add("LibJS/Runtime/ValueInlines.h")
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
includes.add(iterator_implementation_header_for_interface(interface))
iterator_interface_name = f"{interface.name}Iterator"
out.write(f"""GC_DEFINE_ALLOCATOR({interface.name}IteratorPrototype);
{interface.name}IteratorPrototype::{interface.name}IteratorPrototype(JS::Realm& realm)
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().iterator_prototype())
{{
}}
{interface.name}IteratorPrototype::~{interface.name}IteratorPrototype()
{{
}}
void {interface.name}IteratorPrototype::initialize(JS::Realm& realm)
{{
auto& vm = this->vm();
Base::initialize(realm);
define_native_function(realm, vm.names.next, next, 0, JS::Attribute::Writable | JS::Attribute::Enumerable | JS::Attribute::Configurable);
define_direct_property(vm.well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm, "{interface.name} Iterator"_utf16), JS::Attribute::Configurable);
}}
static JS::ThrowCompletionOr<{fully_qualified_name_for_interface(interface)}Iterator*> {make_name_acceptable_cpp(title_case_to_snake_case(iterator_interface_name))}_impl_from(JS::VM& vm)
{{
auto this_object = TRY(vm.this_value().to_object(vm));
if (!is<{fully_qualified_name_for_interface(interface)}Iterator>(*this_object))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "{iterator_interface_name}");
return static_cast<{fully_qualified_name_for_interface(interface)}Iterator*>(this_object.ptr());
}}
JS_DEFINE_NATIVE_FUNCTION({interface.name}IteratorPrototype::next)
{{
WebIDL::log_trace(vm, "{interface.name}IteratorPrototype::next");
auto* impl = TRY({make_name_acceptable_cpp(title_case_to_snake_case(iterator_interface_name))}_impl_from(vm));
return TRY(throw_dom_exception_if_needed(vm, [&] {{ return impl->next(); }}));
}}
""")
def write_async_iterable_declaration_functions(
out: TextIO,
context: GenerationContext,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.async_iterable is None:
return
if interface.async_iterable.key_type is not None:
raise RuntimeError(f"Unsupported pair async iterable declaration on '{interface.name}'")
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
includes.add(async_iterator_implementation_header_for_interface(interface))
add_header_includes_for_idl_type(interface.async_iterable.value_type, includes, context)
out.write(
f"""JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::values)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::values");
auto& realm = *vm.current_realm();
auto* impl = TRY(impl_from(vm));
"""
)
write_operation_parameter_conversions(out, interface.async_iterable.parameters, includes, context)
arguments = ", ".join(idl_identifier_cpp_name(parameter) for parameter in interface.async_iterable.parameters)
if arguments:
arguments = f", {arguments}"
out.write(
f""" return TRY(throw_dom_exception_if_needed(vm, [&] {{ return {fully_qualified_name_for_interface(interface)}AsyncIterator::create(realm, JS::Object::PropertyKind::Value, *impl{arguments}); }}));
}}
"""
)
def write_async_iterator_prototype_implementation(
out: TextIO,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.async_iterable is None:
return
includes.add("AK/StringView.h")
includes.add("LibJS/Runtime/AsyncIteratorPrototype.h")
includes.add("LibJS/Runtime/PrimitiveString.h")
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
includes.add("LibWeb/WebIDL/AsyncIterator.h")
includes.add(async_iterator_implementation_header_for_interface(interface))
out.write(
f"""GC_DEFINE_ALLOCATOR({interface.name}AsyncIteratorPrototype);
{interface.name}AsyncIteratorPrototype::{interface.name}AsyncIteratorPrototype(JS::Realm& realm)
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().async_iterator_prototype())
{{
}}
{interface.name}AsyncIteratorPrototype::~{interface.name}AsyncIteratorPrototype()
{{
}}
void {interface.name}AsyncIteratorPrototype::initialize(JS::Realm& realm)
{{
auto& vm = this->vm();
Base::initialize(realm);
define_direct_property(vm.well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm, "{interface.name} AsyncIterator"_utf16), JS::Attribute::Configurable);
define_native_function(realm, vm.names.next, next, 0, JS::default_attributes);
{"define_native_function(realm, vm.names.return_, return_, 1, JS::default_attributes);" if "DefinesAsyncIteratorReturn" in interface.extended_attributes else ""}
}}
JS_DEFINE_NATIVE_FUNCTION({interface.name}AsyncIteratorPrototype::next)
{{
WebIDL::log_trace(vm, "{interface.name}AsyncIteratorPrototype::next");
auto& realm = *vm.current_realm();
return TRY(throw_dom_exception_if_needed(vm, [&] {{
return WebIDL::AsyncIterator::next<{fully_qualified_name_for_interface(interface)}AsyncIterator>(realm, "{interface.name}AsyncIterator"sv);
}}));
}}
"""
)
if "DefinesAsyncIteratorReturn" in interface.extended_attributes:
out.write(f"""
JS_DEFINE_NATIVE_FUNCTION({interface.name}AsyncIteratorPrototype::return_)
{{
WebIDL::log_trace(vm, "{interface.name}AsyncIteratorPrototype::return");
auto& realm = *vm.current_realm();
auto value = vm.argument(0);
return TRY(throw_dom_exception_if_needed(vm, [&] {{
return WebIDL::AsyncIterator::return_<{fully_qualified_name_for_interface(interface)}AsyncIterator>(realm, "{interface.name}AsyncIterator"sv, value);
}}));
}}
""")
def write_maplike_declaration_functions(
out: TextIO,
context: GenerationContext,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.maplike is None:
return
includes.add("LibJS/Runtime/AbstractOperations.h")
includes.add("LibJS/Runtime/Error.h")
includes.add("LibJS/Runtime/Map.h")
includes.add("LibJS/Runtime/MapIterator.h")
includes.add("LibJS/Runtime/ValueInlines.h")
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
out.write(f"""// https://webidl.spec.whatwg.org/#js-map-size
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::get_size)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::get_size");
// 1. Let O be the this value, implementation-checked against A with identifier "size" and type "getter".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Return maps size, converted to a JavaScript value.
return map->map_size();
}}
// https://webidl.spec.whatwg.org/#js-map-entries
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::entries)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::entries");
auto& realm = *vm.current_realm();
// 1. Let O be the this value, implementation-checked against A with identifier "entries" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Return the result of creating a map iterator from map with kind "key+value".
return JS::MapIterator::create(realm, *map, PropertyKind::KeyAndValue);
}}
// https://webidl.spec.whatwg.org/#js-map-keys
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::keys)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::keys");
auto& realm = *vm.current_realm();
// 1. Let O be the this value, implementation-checked against A with identifier "keys" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Return the result of creating a map iterator from map with kind "key".
return JS::MapIterator::create(realm, *map, PropertyKind::Key);
}}
// https://webidl.spec.whatwg.org/#js-map-values
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::values)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::values");
auto& realm = *vm.current_realm();
// 1. Let O be the this value, implementation-checked against A with identifier "values" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Return the result of creating a map iterator from map with kind "value".
return JS::MapIterator::create(realm, *map, PropertyKind::Value);
}}
// https://webidl.spec.whatwg.org/#js-map-forEach
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::for_each)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::for_each");
// 1. Let O be the this value, implementation-checked against A with identifier "forEach" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Let callbackFn be the first argument passed to the function, or undefined if not supplied.
auto callback = vm.argument(0);
// 4. If IsCallable(callbackFn) is false, throw a TypeError.
if (!callback.is_function())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, callback);
// 5. Let thisArg be the second argument passed to the function, or undefined if not supplied.
auto this_arg = vm.argument(1);
// 6. For each key → value of map:
for (auto& [key, value] : *map) {{
// 1. Let jsKey and jsValue be key and value converted to a JavaScript value.
// 2. Perform ? Call(callbackFn, thisArg, « jsValue, jsKey, O »).
TRY(JS::call(vm, callback.as_function(), this_arg, value, key, this_impl));
}}
// 7. Return undefined.
return JS::js_undefined();
}}
// https://webidl.spec.whatwg.org/#js-map-get
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::get)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::get");
// 1. Let O be the this value, implementation-checked against A with identifier "get" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Let keyType be the key type specified in the maplike declaration.
// 4. Let keyArg be the first argument passed to this function, or undefined if not supplied.
// 5. Let key be keyArg converted to an IDL value of type keyType.
auto key = vm.argument(0);
{type_check_idl_value(interface.maplike.key_type, "key", includes, context, interface.name)}
// FIXME: 6. If key is -0, set key to +0.
// 7. If map[key] exists, then return map[key], converted to a JavaScript value.
auto result = map->map_get(key);
return result.value_or(JS::js_undefined());
}}
// https://webidl.spec.whatwg.org/#js-map-has
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::has)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::has");
// 1. Let O be the this value, implementation-checked against A with identifier "has" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Let keyType be the key type specified in the maplike declaration.
// 4. Let keyArg be the first argument passed to this function, or undefined if not supplied.
// 5. Let key be keyArg converted to an IDL value of type keyType.
auto key = vm.argument(0);
{type_check_idl_value(interface.maplike.key_type, "key", includes, context, interface.name)}
// FIXME: 6. If key is -0, set key to +0.
// 7. If map[key] exists, then return true; otherwise return false.
return map->map_has(key);
}}
""")
# If A does not declare a member with identifier "set", and A was declared with a readwrite maplike declaration,
# then there must exist a set data property on As interface prototype object with the following characteristics:
if "set" not in operation_callback_names(interface) and not interface.maplike.readonly:
out.write(f"""// https://webidl.spec.whatwg.org/#js-map-set
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::set)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::set");
// 1. Let O be the this value, implementation-checked against A with identifier "set" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Let keyType be the key type specified in the maplike declaration, and valueType be the value type.
// 4. Let keyArg be the first argument passed to this function, or undefined if not supplied.
// 5. Let key be keyArg converted to an IDL value of type keyType.
auto key = vm.argument(0);
{type_check_idl_value(interface.maplike.key_type, "key", includes, context, interface.name)}
// FIXME: 6. If key is -0, set key to +0.
// 7. Let valueArg be the second argument passed to this function, or undefined if not supplied.
// 8. Let value be valueArg converted to an IDL value of type valueType.
auto value = vm.argument(1);
{type_check_idl_value(interface.maplike.value_type, "value", includes, context, interface.name)}
// 9. Set map[key] to value.
map->map_set(key, value);
this_impl->on_map_modified_from_js({{}});
// 10. Return O.
return this_impl;
}}
""")
# If A does not declare a member with identifier "delete", and A was declared with a readwrite maplike declaration,
# then there must exist a delete data property on As interface prototype object with the following characteristics:
if "delete_" not in operation_callback_names(interface) and not interface.maplike.readonly:
out.write(f"""// https://webidl.spec.whatwg.org/#js-map-delete
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::delete_)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::delete_");
// 1. Let O be the this value, implementation-checked against A with identifier "delete" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Let keyType be the key type specified in the maplike declaration.
// 4. Let keyArg be the first argument passed to this function, or undefined if not supplied.
// 5. Let key be keyArg converted to an IDL value of type keyType.
auto key = vm.argument(0);
{type_check_idl_value(interface.maplike.key_type, "key", includes, context, interface.name)}
// FIXME: 6. If key is -0, set key to +0.
// 7. Let retVal be true if map[key] exists, or else false.
// 8. Remove map[key].
auto result = map->map_remove(key);
this_impl->on_map_modified_from_js({{}});
// 9. Return retVal.
return result;
}}
""")
# If A does not declare a member with identifier "clear", and A was declared with a readwrite maplike declaration,
# then there must exist a clear data property on As interface prototype object with the following characteristics:
if "clear" not in operation_callback_names(interface) and not interface.maplike.readonly:
out.write(f"""// https://webidl.spec.whatwg.org/#js-map-delete
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::clear)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::clear");
// 1. Let O be the this value, implementation-checked against A with identifier "delete" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let map be the map entries of the IDL value that represents a reference to O.
GC::Ref<JS::Map> map = this_impl->map_entries();
// 3. Clear map.
// NOTE: The map is preserved because there may be existing iterators, currently suspended, iterating over it.
map->map_clear();
this_impl->on_map_modified_from_js({{}});
// 4. Return undefined.
return JS::js_undefined();
}}
""")
def write_setlike_declaration_functions(
out: TextIO,
context: GenerationContext,
includes: GeneratedIncludes,
interface: Interface,
) -> None:
if interface.setlike is None:
return
includes.add("LibJS/Runtime/AbstractOperations.h")
includes.add("LibJS/Runtime/Error.h")
includes.add("LibJS/Runtime/Set.h")
includes.add("LibJS/Runtime/SetIterator.h")
includes.add("LibJS/Runtime/ValueInlines.h")
includes.add("LibWeb/Bindings/ExceptionOrUtils.h")
out.write(f"""// https://webidl.spec.whatwg.org/#js-set-size
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::get_size)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::size");
// 1. Let O be the this value, implementation-checked against A with identifier "size" and type "getter".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be the set entries of the IDL value that represents a reference to O.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Return sets size, converted to a JavaScript value.
return set->set_size();
}}
// https://webidl.spec.whatwg.org/#js-set-entries
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::entries)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::values");
auto& realm = *vm.current_realm();
// 1. Let O be the this value, implementation-checked against A with identifier "entries" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be the set entries of the IDL value that represents a reference to O.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Return the result of creating a set iterator from set with kind "key+value".
return JS::SetIterator::create(realm, *set, PropertyKind::KeyAndValue);
}}
// https://webidl.spec.whatwg.org/#js-set-values
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::values)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::values");
auto& realm = *vm.current_realm();
// 1. Let O be the this value, implementation-checked against A with identifier "values" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be the set entries of the IDL value that represents a reference to O.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Return the result of creating a set iterator from set with kind "value".
return JS::SetIterator::create(realm, *set, PropertyKind::Value);
}}
// https://webidl.spec.whatwg.org/#js-set-forEach
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::for_each)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::for_each");
// 1. Let O be the this value, implementation-checked against A with identifier "forEach" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be the set entries of the IDL value that represents a reference to O.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Let callbackFn be the first argument passed to the function, or undefined if not supplied.
// 4. If IsCallable(callbackFn) is false, throw a TypeError.
auto callback = vm.argument(0);
if (!callback.is_function())
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, callback);
// 5. Let thisArg be the second argument passed to the function, or undefined if not supplied.
auto this_arg = vm.argument(1);
// 6. For each value of set:
for (auto& entry : *set) {{
// 1. Let jsValue be value converted to a JavaScript value.
auto value = entry.key;
// 2. Perform ? Call(callbackFn, thisArg, « jsValue, jsValue, O»).
TRY(JS::call(vm, callback.as_function(), this_arg, value, value, this_impl));
}}
// 7. Return undefined.
return JS::js_undefined();
}}
// https://webidl.spec.whatwg.org/#js-set-has
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::has)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::has");
// 1. Let O be the this value, implementation-checked against A with identifier "has" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be the set entries of the IDL value that represents a reference to O.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Let valueType be the value type specified in the setlike declaration.
// 4. Let valueArg be the first argument passed to this function, or undefined if not supplied.
// 5. Let value be valueArg converted to an IDL value of type valueType.
// FIXME: 6. If value is -0, set value to +0.
auto value = vm.argument(0);
{type_check_idl_value(interface.setlike.value_type, "value", includes, context, interface.name)}
// 7. If set contains value, then return true, otherwise return false.
return set->set_has(value);
}}
""")
# If A does not declare a member with identifier "add", and A was declared with a readwrite setlike declaration,
# then there must exist an add data property on As interface prototype object with the following characteristics:
if "add" not in operation_callback_names(interface) and not interface.setlike.readonly:
out.write(f"""// https://webidl.spec.whatwg.org/#js-set-add
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::add)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::add");
// 1. Let O be the this value, implementation-checked against A with identifier "add" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be the set entries of the IDL value that represents a reference to O.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Let valueType be the value type specified in the setlike declaration.
// 4. Let valueArg be the first argument passed to this function, or undefined if not supplied.
// 5. Let value be valueArg converted to an IDL value of type valueType.
// FIXME: 6. If value is -0, set value to +0.
auto value = vm.argument(0);
{type_check_idl_value(interface.setlike.value_type, "value", includes, context, interface.name)}
// 6. Append value to set.
set->set_add(value);
this_impl->on_set_modified_from_js({{}});
return this_impl;
}}
""")
# If A does not declare a member with identifier "delete", and A was declared with a readwrite setlike declaration,
# then there must exist a delete data property on As interface prototype object with the following characteristics:
if "delete_" not in operation_callback_names(interface) and not interface.setlike.readonly:
out.write(f"""// https://webidl.spec.whatwg.org/#js-set-delete
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::delete_)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::delete_");
// 1. Let O be the this value, implementation-checked against A with identifier "delete" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be Os set entries.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Let valueType be the value type specified in the setlike declaration.
// 4. Let valueArg be the first argument passed to this function, or undefined if not supplied.
// 5. Let value be valueArg converted to an IDL value of type valueType.
// 6. FIXME: If value is -0, set value to +0.
auto value = vm.argument(0);
{type_check_idl_value(interface.setlike.value_type, "value", includes, context, interface.name)}
// 7. Let retVal be true if set contains value, or else false.
// 8. Remove value from set.
auto result = set->set_remove(value);
this_impl->on_set_modified_from_js({{}});
// 9. Return retVal.
return result;
}}
""")
# If A does not declare a member with identifier "clear", and A was declared with a readwrite setlike declaration,
# then there must exist a clear data property on As interface prototype object with the following characteristics:
if "clear" not in operation_callback_names(interface) and not interface.setlike.readonly:
out.write(f"""// https://webidl.spec.whatwg.org/#js-set-clear
JS_DEFINE_NATIVE_FUNCTION({interface.prototype_class}::clear)
{{
WebIDL::log_trace(vm, "{interface.prototype_class}::clear");
// 1. Let O be the this value, implementation-checked against A with identifier "clear" and type "method".
auto* this_impl = TRY(impl_from(vm));
// 2. Let set be the set entries of the IDL value that represents a reference to O.
GC::Ref<JS::Set> set = this_impl->set_entries();
// 3. Empty set.
// NOTE: Note: The set is preserved because there may be existing iterators, currently suspended, iterating over it.
set->set_clear();
this_impl->on_set_modified_from_js({{}});
// 4. Return undefined.
return JS::js_undefined();
}}
""")