mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-05 17:16:04 +00:00
LibIDL+LibWeb: Add support for WebIDL setlike declarations on interfaces
This commit is contained in:
parent
f29ac8517e
commit
7077e4d002
Notes:
sideshowbarker
2024-07-17 16:23:55 +09:00
Author: https://github.com/ADKaster
Commit: 7077e4d002
Pull-request: https://github.com/SerenityOS/serenity/pull/24413
Reviewed-by: https://github.com/awesomekling
4 changed files with 237 additions and 0 deletions
|
@ -2643,6 +2643,33 @@ static void generate_prototype_or_global_mixin_declarations(IDL::Interface const
|
|||
)~~~");
|
||||
}
|
||||
|
||||
if (interface.set_entry_type.has_value()) {
|
||||
auto setlike_generator = generator.fork();
|
||||
|
||||
setlike_generator.append(R"~~~(
|
||||
JS_DECLARE_NATIVE_FUNCTION(get_size);
|
||||
JS_DECLARE_NATIVE_FUNCTION(entries);
|
||||
JS_DECLARE_NATIVE_FUNCTION(values);
|
||||
JS_DECLARE_NATIVE_FUNCTION(for_each);
|
||||
JS_DECLARE_NATIVE_FUNCTION(has);
|
||||
)~~~");
|
||||
if (!interface.overload_sets.contains("add"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
JS_DECLARE_NATIVE_FUNCTION(add);
|
||||
)~~~");
|
||||
}
|
||||
if (!interface.overload_sets.contains("delete"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
JS_DECLARE_NATIVE_FUNCTION(delete_);
|
||||
)~~~");
|
||||
}
|
||||
if (!interface.overload_sets.contains("clear"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
JS_DECLARE_NATIVE_FUNCTION(clear);
|
||||
)~~~");
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& attribute : interface.attributes) {
|
||||
auto attribute_generator = generator.fork();
|
||||
attribute_generator.set("attribute.name:snakecase", attribute.name.to_snakecase());
|
||||
|
@ -3172,6 +3199,39 @@ void @class_name@::initialize(JS::Realm& realm)
|
|||
)~~~");
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#js-setlike
|
||||
if (interface.set_entry_type.has_value()) {
|
||||
|
||||
auto setlike_generator = generator.fork();
|
||||
|
||||
setlike_generator.append(R"~~~(
|
||||
define_native_accessor(realm, vm.names.size, get_size, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable);
|
||||
define_native_function(realm, vm.names.entries, entries, 0, default_attributes);
|
||||
// NOTE: Keys intentionally returns values for setlike
|
||||
define_native_function(realm, vm.names.keys, values, 0, default_attributes);
|
||||
define_native_function(realm, vm.names.values, values, 0, default_attributes);
|
||||
define_direct_property(vm.well_known_symbol_iterator(), get_without_side_effects(vm.names.values), JS::Attribute::Configurable | JS::Attribute::Writable);
|
||||
define_native_function(realm, vm.names.forEach, for_each, 1, default_attributes);
|
||||
define_native_function(realm, vm.names.has, has, 1, default_attributes);
|
||||
)~~~");
|
||||
|
||||
if (!interface.overload_sets.contains("add"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
define_native_function(realm, vm.names.add, add, 1, default_attributes);
|
||||
)~~~");
|
||||
}
|
||||
if (!interface.overload_sets.contains("delete"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
define_native_function(realm, vm.names.delete_, delete_, 1, default_attributes);
|
||||
)~~~");
|
||||
}
|
||||
if (!interface.overload_sets.contains("clear"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
define_native_function(realm, vm.names.clear, clear, 0, default_attributes);
|
||||
)~~~");
|
||||
}
|
||||
}
|
||||
|
||||
if (interface.has_unscopable_member) {
|
||||
generator.append(R"~~~(
|
||||
define_direct_property(vm.well_known_symbol_unscopables(), unscopable_object, JS::Attribute::Configurable);
|
||||
|
@ -3771,6 +3831,149 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::values)
|
|||
}
|
||||
)~~~");
|
||||
}
|
||||
|
||||
if (interface.set_entry_type.has_value()) {
|
||||
auto setlike_generator = generator.fork();
|
||||
setlike_generator.set("value_type", interface.set_entry_type.value()->name());
|
||||
setlike_generator.append(R"~~~(
|
||||
// https://webidl.spec.whatwg.org/#js-set-size
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::get_size)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::size");
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
return set->set_size();
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#js-set-entries
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::entries)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::entries");
|
||||
auto& realm = *vm.current_realm();
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
return TRY(throw_dom_exception_if_needed(vm, [&] { return JS::SetIterator::create(realm, *set, Object::PropertyKind::KeyAndValue); }));
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#js-set-values
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::values)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::values");
|
||||
auto& realm = *vm.current_realm();
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
return TRY(throw_dom_exception_if_needed(vm, [&] { return JS::SetIterator::create(realm, *set, Object::PropertyKind::Value); }));
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#js-set-forEach
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::for_each)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::for_each");
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
auto callback = vm.argument(0);
|
||||
if (!callback.is_function())
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAFunction, callback.to_string_without_side_effects());
|
||||
|
||||
for (auto& entry : *set) {
|
||||
auto value = entry.key;
|
||||
TRY(call(vm, callback.as_function(), vm.argument(1), value, value, impl));
|
||||
}
|
||||
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#js-set-has
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::has)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::has");
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
auto value_arg = vm.argument(0);
|
||||
if (!value_arg.is_object() && !is<@value_type@>(value_arg.as_object())) {
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@value_type@");
|
||||
}
|
||||
|
||||
// FIXME: If value is -0, set value to +0.
|
||||
// What? Which interfaces have a number as their set type?
|
||||
|
||||
return set->set_has(value_arg);
|
||||
}
|
||||
)~~~");
|
||||
|
||||
if (!interface.overload_sets.contains("add"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
// https://webidl.spec.whatwg.org/#js-set-add
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::add)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::add");
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
auto value_arg = vm.argument(0);
|
||||
if (!value_arg.is_object() && !is<@value_type@>(value_arg.as_object())) {
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@value_type@");
|
||||
}
|
||||
|
||||
// FIXME: If value is -0, set value to +0.
|
||||
// What? Which interfaces have a number as their set type?
|
||||
|
||||
set->set_add(value_arg);
|
||||
|
||||
return impl;
|
||||
}
|
||||
)~~~");
|
||||
}
|
||||
if (!interface.overload_sets.contains("delete"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
// https://webidl.spec.whatwg.org/#js-set-delete
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::delete_)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::delete_");
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
auto value_arg = vm.argument(0);
|
||||
if (!value_arg.is_object() && !is<@value_type@>(value_arg.as_object())) {
|
||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@value_type@");
|
||||
}
|
||||
|
||||
// FIXME: If value is -0, set value to +0.
|
||||
// What? Which interfaces have a number as their set type?
|
||||
|
||||
return set->set_remove(value_arg);
|
||||
}
|
||||
)~~~");
|
||||
}
|
||||
if (!interface.overload_sets.contains("clear"sv) && !interface.is_set_readonly) {
|
||||
setlike_generator.append(R"~~~(
|
||||
// https://webidl.spec.whatwg.org/#js-set-clear
|
||||
JS_DEFINE_NATIVE_FUNCTION(@class_name@::clear)
|
||||
{
|
||||
WebIDL::log_trace(vm, "@class_name@::clear");
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
|
||||
JS::NonnullGCPtr<JS::Set> set = impl->set_entries();
|
||||
|
||||
set->set_clear();
|
||||
|
||||
return JS::js_undefined();
|
||||
}
|
||||
)~~~");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_namespace_header(IDL::Interface const& interface, StringBuilder& builder)
|
||||
|
|
|
@ -292,8 +292,11 @@ void Parser::parse_attribute(HashMap<ByteString, ByteString>& extended_attribute
|
|||
if (readonly)
|
||||
consume_whitespace();
|
||||
|
||||
// FIXME: Should we parse 'readonly setlike<T>' differently than this?
|
||||
if (lexer.consume_specific("attribute"sv))
|
||||
consume_whitespace();
|
||||
else if (lexer.consume_specific("setlike"sv) && !inherit)
|
||||
parse_setlike(interface, readonly);
|
||||
else
|
||||
report_parsing_error("expected 'attribute'"sv, filename, input, lexer.tell());
|
||||
|
||||
|
@ -466,6 +469,28 @@ void Parser::parse_iterable(Interface& interface)
|
|||
|
||||
interface.value_iterator_type = move(first_type);
|
||||
}
|
||||
|
||||
if (interface.set_entry_type.has_value())
|
||||
report_parsing_error("Interfaces with an iterable declaration must not have a setlike declaration."sv, filename, input, lexer.tell());
|
||||
|
||||
assert_specific('>');
|
||||
assert_specific(';');
|
||||
}
|
||||
|
||||
void Parser::parse_setlike(Interface& interface, bool is_readonly)
|
||||
{
|
||||
if (interface.supports_indexed_properties())
|
||||
report_parsing_error("Interfaces with a setlike declaration must not supported indexed properties."sv, filename, input, lexer.tell());
|
||||
|
||||
if (interface.value_iterator_type.has_value() || interface.pair_iterator_types.has_value())
|
||||
report_parsing_error("Interfaces with a setlike declaration must not must not be iterable."sv, filename, input, lexer.tell());
|
||||
|
||||
assert_string("setlike"sv);
|
||||
assert_specific('<');
|
||||
|
||||
interface.set_entry_type = parse_type();
|
||||
interface.is_set_readonly = is_readonly;
|
||||
|
||||
assert_specific('>');
|
||||
assert_specific(';');
|
||||
}
|
||||
|
@ -625,6 +650,12 @@ void Parser::parse_interface(Interface& interface)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("setlike")) {
|
||||
bool is_readonly = false;
|
||||
parse_setlike(interface, is_readonly);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("inherit") || lexer.next_is("readonly") || lexer.next_is("attribute")) {
|
||||
parse_attribute(extended_attributes, interface);
|
||||
continue;
|
||||
|
|
|
@ -58,6 +58,7 @@ private:
|
|||
void parse_deleter(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
|
||||
void parse_stringifier(HashMap<ByteString, ByteString>& extended_attributes, Interface&);
|
||||
void parse_iterable(Interface&);
|
||||
void parse_setlike(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();
|
||||
|
|
|
@ -281,6 +281,8 @@ public:
|
|||
|
||||
Optional<NonnullRefPtr<Type const>> value_iterator_type;
|
||||
Optional<Tuple<NonnullRefPtr<Type const>, NonnullRefPtr<Type const>>> pair_iterator_types;
|
||||
Optional<NonnullRefPtr<Type const>> set_entry_type;
|
||||
bool is_set_readonly { false };
|
||||
|
||||
Optional<Function> named_property_getter;
|
||||
Optional<Function> named_property_setter;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue