IDLGenerators: Fix Exposed extended attribute codegen

This commit is contained in:
Bastiaan van der Plaat 2025-05-05 20:24:15 +02:00 committed by Andrew Kaster
commit 1a7932601a
Notes: github-actions[bot] 2025-06-30 17:40:34 +00:00
3 changed files with 89 additions and 10 deletions

View file

@ -17,6 +17,7 @@
#include <AK/NumericLimits.h>
#include <AK/Queue.h>
#include <AK/QuickSort.h>
#include <LibIDL/ExposedTo.h>
#include <LibIDL/Types.h>
namespace IDL {
@ -3500,22 +3501,34 @@ void @class_name@::initialize(JS::Realm& realm)
)~~~");
}
// NOTE: Add more specified exposed global interface groups when needed.
StringBuilder window_exposed_only_members_builder;
SourceGenerator window_exposed_only_members_generator { window_exposed_only_members_builder, generator.clone_mapping() };
auto generator_for_member = [&](auto const& name, auto& extended_attributes) -> SourceGenerator {
if (auto maybe_exposed = extended_attributes.get("Exposed"); maybe_exposed.has_value()) {
auto exposed_to = MUST(IDL::parse_exposure_set(name, *maybe_exposed));
if (exposed_to == IDL::ExposedTo::Window) {
return window_exposed_only_members_generator.fork();
}
}
return generator.fork();
};
// https://webidl.spec.whatwg.org/#es-attributes
for (auto& attribute : interface.attributes) {
bool has_unforgeable_attribute = attribute.extended_attributes.contains("LegacyUnforgeable"sv);
if ((generate_unforgeables == GenerateUnforgeables::Yes && !has_unforgeable_attribute) || (generate_unforgeables == GenerateUnforgeables::No && has_unforgeable_attribute))
continue;
auto attribute_generator = generator_for_member(attribute.name, attribute.extended_attributes);
if (attribute.extended_attributes.contains("FIXME")) {
auto fixme_attribute_generator = generator.fork();
fixme_attribute_generator.set("attribute.name", attribute.name);
fixme_attribute_generator.append(R"~~~(
attribute_generator.set("attribute.name", attribute.name);
attribute_generator.append(R"~~~(
@define_direct_property@("@attribute.name@"_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented);
)~~~");
continue;
}
auto attribute_generator = generator.fork();
attribute_generator.set("attribute.name", attribute.name);
attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name);
@ -3541,9 +3554,9 @@ void @class_name@::initialize(JS::Realm& realm)
continue;
if (function.extended_attributes.contains("FIXME")) {
auto fixme_function_generator = generator.fork();
fixme_function_generator.set("function.name", function.name);
fixme_function_generator.append(R"~~~(
auto function_generator = generator_for_member(function.name, function.extended_attributes);
function_generator.set("function.name", function.name);
function_generator.append(R"~~~(
@define_direct_property@("@function.name@"_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented);
)~~~");
}
@ -3572,7 +3585,8 @@ void @class_name@::initialize(JS::Realm& realm)
if ((generate_unforgeables == GenerateUnforgeables::Yes && !has_unforgeable_attribute) || (generate_unforgeables == GenerateUnforgeables::No && has_unforgeable_attribute))
continue;
auto function_generator = generator.fork();
auto const& function = overload_set.value.first();
auto function_generator = generator_for_member(function.name, function.extended_attributes);
function_generator.set("function.name", overload_set.key);
function_generator.set("function.name:snakecase", make_input_acceptable_cpp(overload_set.key.to_snakecase()));
function_generator.set("function.length", ByteString::number(get_shortest_function_length(overload_set.value)));
@ -3595,10 +3609,11 @@ void @class_name@::initialize(JS::Realm& realm)
if ((generate_unforgeables == GenerateUnforgeables::Yes && !has_unforgeable_attribute) || (generate_unforgeables == GenerateUnforgeables::No && has_unforgeable_attribute))
should_generate_stringifier = false;
}
if (interface.has_stringifier && should_generate_stringifier) {
// FIXME: Do stringifiers need to be added to the unscopable list?
auto stringifier_generator = generator.fork();
auto stringifier_generator = interface.stringifier_extended_attributes.has_value()
? generator_for_member("stringifier"sv, *interface.stringifier_extended_attributes)
: generator.fork();
stringifier_generator.append(R"~~~(
@define_native_function@(realm, "toString"_fly_string, to_string, 0, default_attributes);
)~~~");
@ -3691,6 +3706,16 @@ void @class_name@::initialize(JS::Realm& realm)
)~~~");
}
if (!window_exposed_only_members_generator.as_string_view().is_empty()) {
auto window_only_property_declarations = generator.fork();
window_only_property_declarations.set("defines", window_exposed_only_members_generator.as_string_view());
window_only_property_declarations.append(R"~~~(
if (is<HTML::Window>(realm.global_object())) {
@defines@
}
)~~~");
}
if (!define_on_existing_object) {
generator.append(R"~~~(
Base::initialize(realm);

View file

@ -0,0 +1,6 @@
1. {"a":1,"b":2,"c":3,"d":4,"e":5,"f":6,"m11":1,"m12":2,"m13":0,"m14":0,"m21":3,"m22":4,"m23":0,"m24":0,"m31":0,"m32":0,"m33":1,"m34":0,"m41":5,"m42":6,"m43":0,"m44":1,"is2D":true,"isIdentity":false}
2. "matrix(1, 0, 0, 1, 0, 0)"
3. "Google Inc."
4. TypeError: undefined is not a function (evaluated from '<object>.setMatrixValue')
5. [object DOMMatrix]
6. undefined

View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
asyncTest(async done => {
let testCounter = 1;
function testPart(part) {
println(`${testCounter++}. ${JSON.stringify(part())}`);
}
// 1. In this Window environment, check if DOMMatrix setMatrixValue is available.
testPart(() => new DOMMatrix().setMatrixValue("matrix(1, 2, 3, 4, 5, 6)"));
// 2. In this Window environment, check if DOMMatrix toString has custom behavior.
testPart(() => new DOMMatrix().toString());
// 3. In this Window environment, check if navigator vendor toString is available.
testPart(() => navigator.vendor);
const worker = new Worker(URL.createObjectURL(new Blob([`
// 4. In this new Worker environment, check if DOMMatrix setMatrixValue doesn't exists.
try {
postMessage(new DOMMatrix().setMatrixValue("matrix(1, 2, 3, 4, 5, 6)"));
} catch (error) {
postMessage(error);
}
// 5. In this Worker environment, check if DOMMatrix toString doesn't have custom behavior.
try {
postMessage(new DOMMatrix().toString());
} catch (error) {
postMessage(error);
}
// 6. In this Worker environment, check if navigator vendor doesn't exists.
try {
postMessage(navigator.vendor);
} catch (error) {
postMessage(error);
}
`], { type: 'application/javascript' })));
worker.onmessage = (result) => {
println(`${testCounter++}. ${result.data}`);
if (testCounter === 7) {
done();
}
};
});
</script>