1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 03:10:46 +00:00

IDLGenerators: Handle restricted/unrestricted floating point types

This commit is contained in:
Matthew Olsson 2024-05-27 08:04:58 -07:00 committed by Andreas Kling
parent 2bd8093449
commit 3e221fbb2d
8 changed files with 108 additions and 15 deletions

View File

@ -165,10 +165,10 @@ CppType idl_type_name_to_cpp_type(Type const& type, Interface const& interface)
if (type.is_string())
return { .name = "String", .sequence_storage_type = SequenceStorageType::Vector };
if (type.name() == "double" && !type.is_nullable())
if ((type.name() == "double" || type.name() == "unrestricted double") && !type.is_nullable())
return { .name = "double", .sequence_storage_type = SequenceStorageType::Vector };
if (type.name() == "float" && !type.is_nullable())
if ((type.name() == "float" || type.name() == "unrestricted float") && !type.is_nullable())
return { .name = "float", .sequence_storage_type = SequenceStorageType::Vector };
if (type.name() == "boolean" && !type.is_nullable())
@ -462,6 +462,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
scoped_generator.set("legacy_null_to_empty_string", legacy_null_to_empty_string ? "true" : "false");
scoped_generator.set("string_type", string_to_fly_string ? "FlyString" : "String");
scoped_generator.set("parameter.type.name", parameter.type->name());
scoped_generator.set("parameter.name", parameter.name);
if (explicit_null) {
if (!IDL::is_platform_object(*parameter.type)) {
@ -559,7 +560,14 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
)~~~");
}
}
} else if (parameter.type->name() == "double" || parameter.type->name() == "float") {
} else if (parameter.type->is_floating_point()) {
if (parameter.type->name() == "unrestricted float") {
scoped_generator.set("parameter.type.name", "float");
} else if (parameter.type->name() == "unrestricted double") {
scoped_generator.set("parameter.type.name", "double");
}
bool is_wrapped_in_optional_type = false;
if (!optional) {
scoped_generator.append(R"~~~(
@parameter.type.name@ @cpp_name@ = TRY(@js_name@@js_suffix@.to_double(vm));
@ -570,6 +578,7 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
@parameter.type.name@ @cpp_name@;
)~~~");
} else {
is_wrapped_in_optional_type = true;
scoped_generator.append(R"~~~(
Optional<@parameter.type.name@> @cpp_name@;
)~~~");
@ -588,6 +597,22 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
)~~~");
}
}
if (parameter.type->is_restricted_floating_point()) {
if (is_wrapped_in_optional_type) {
scoped_generator.append(R"~~~(
if (@cpp_name@.has_value() && (isinf(*@cpp_name@) || isnan(*@cpp_name@))) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "@parameter.name@");
}
)~~~");
} else {
scoped_generator.append(R"~~~(
if (isinf(@cpp_name@) || isnan(@cpp_name@)) {
return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidRestrictedFloatingPointParameter, "@parameter.name@");
}
)~~~");
}
}
} else if (parameter.type->name() == "Promise") {
// NOTE: It's not clear to me where the implicit wrapping of non-Promise values in a resolved
// Promise is defined in the spec; https://webidl.spec.whatwg.org/#idl-promise doesn't say
@ -1702,7 +1727,7 @@ static void generate_wrap_statement(SourceGenerator& generator, ByteString const
@result_expression@ new_array@recursion_depth@;
)~~~");
} else if (type.name() == "boolean" || type.name() == "double" || type.name() == "float") {
} else if (type.name() == "boolean" || type.is_floating_point()) {
if (type.is_nullable()) {
scoped_generator.append(R"~~~(
@result_expression@ JS::Value(@value@.release_value());

View File

@ -0,0 +1,10 @@
KeyframeEffect with NaN options value: threw an exception
KeyframeEffect with infinite delay value: threw an exception
KeyframeEffect with NaN delay value: threw an exception
KeyframeEffect with infinite endDelay value: threw an exception
KeyframeEffect with NaN endDelay value: threw an exception
KeyframeEffect with NaN iterations: threw an exception
KeyframeEffect with infinite options value: did not throw an exception
KeyframeEffect with finite options value: did not throw an exception
KeyframeEffect with infinite iterations: did not throw an exception
KeyframeEffect with infinite duration: did not throw an exception

View File

@ -3,16 +3,24 @@ Testing DOMRect:
2. {"x":10,"y":20,"width":30,"height":40,"top":20,"right":40,"bottom":60,"left":10}
3. {"x":-10,"y":-20,"width":30,"height":40,"top":-20,"right":20,"bottom":20,"left":-10}
4. {"x":10,"y":20,"width":30,"height":40,"top":20,"right":40,"bottom":60,"left":10}
5. {"x":null,"y":20,"width":30,"height":40,"top":20,"right":null,"bottom":60,"left":null}
6. {"x":10,"y":null,"width":30,"height":40,"top":null,"right":40,"bottom":null,"left":10}
7. {"x":10,"y":20,"width":null,"height":40,"top":20,"right":null,"bottom":60,"left":null}
8. {"x":10,"y":20,"width":30,"height":null,"top":null,"right":40,"bottom":null,"left":10}
5. Exception: TypeError
6. Exception: TypeError
7. Exception: TypeError
8. Exception: TypeError
9. Exception: TypeError
10. Exception: TypeError
11. Exception: TypeError
12. Exception: TypeError
Testing DOMRectReadOnly:
1. {"x":0,"y":0,"width":0,"height":0,"top":0,"right":0,"bottom":0,"left":0}
2. {"x":10,"y":20,"width":30,"height":40,"top":20,"right":40,"bottom":60,"left":10}
3. {"x":-10,"y":-20,"width":30,"height":40,"top":-20,"right":20,"bottom":20,"left":-10}
4. {"x":10,"y":20,"width":30,"height":40,"top":20,"right":40,"bottom":60,"left":10}
5. {"x":null,"y":20,"width":30,"height":40,"top":20,"right":null,"bottom":60,"left":null}
6. {"x":10,"y":null,"width":30,"height":40,"top":null,"right":40,"bottom":null,"left":10}
7. {"x":10,"y":20,"width":null,"height":40,"top":20,"right":null,"bottom":60,"left":null}
8. {"x":10,"y":20,"width":30,"height":null,"top":null,"right":40,"bottom":null,"left":10}
5. Exception: TypeError
6. Exception: TypeError
7. Exception: TypeError
8. Exception: TypeError
9. Exception: TypeError
10. Exception: TypeError
11. Exception: TypeError
12. Exception: TypeError

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<script src="../../include.js"></script>
<script>
test(() => {
function checkException(name, cb) {
try {
cb();
println(`${name}: did not throw an exception`);
} catch (e) {
println(`${name}: threw an exception`);
}
}
// Test invalid values
checkException('KeyframeEffect with NaN options value', () => new KeyframeEffect(null, null, NaN));
checkException('KeyframeEffect with infinite delay value', () => new KeyframeEffect(null, null, { delay: -Infinity }));;
checkException('KeyframeEffect with NaN delay value', () => new KeyframeEffect(null, null, { delay: NaN }));
checkException('KeyframeEffect with infinite endDelay value', () => new KeyframeEffect(null, null, { endDelay: Infinity }));
checkException('KeyframeEffect with NaN endDelay value', () => new KeyframeEffect(null, null, { endDelay: NaN }));
checkException('KeyframeEffect with NaN iterations', () => new KeyframeEffect(null, null, { iterations: NaN }));
// Test valid values
checkException('KeyframeEffect with infinite options value', () => new KeyframeEffect(null, null, Infinity));
checkException('KeyframeEffect with finite options value', () => new KeyframeEffect(null, null, 1234.5));
checkException('KeyframeEffect with infinite iterations', () => new KeyframeEffect(null, null, { iterations: Infinity }));
checkException('KeyframeEffect with infinite duration', () => new KeyframeEffect(null, null, { duration: Infinity }));
});
</script>

View File

@ -38,6 +38,18 @@
// 8. Creating a DOMRect with NaN height value
testPart(() => new Rect(10, 20, 30, NaN));
// 5. Creating a DOMRect with Infinity x value
testPart(() => new Rect(Infinity, 20, 30, 40));
// 6. Creating a DOMRect with Infinity y value
testPart(() => new Rect(10, Infinity, 30, 40));
// 7. Creating a DOMRect with Infinity width value
testPart(() => new Rect(10, 20, Infinity, 40));
// 8. Creating a DOMRect with Infinity height value
testPart(() => new Rect(10, 20, 30, Infinity));
testCounter = 1;
}
});

View File

@ -225,10 +225,12 @@ NonnullRefPtr<Type const> Parser::parse_type()
if (unsigned_)
consume_whitespace();
// FIXME: Actually treat "unrestricted" and normal floats/doubles differently.
if (lexer.consume_specific("unrestricted"sv))
bool unrestricted = lexer.consume_specific("unrestricted"sv);
if (unrestricted)
consume_whitespace();
VERIFY(!(unsigned_ && unrestricted));
auto name = lexer.consume_until([](auto ch) { return !is_ascii_alphanumeric(ch) && ch != '_'; });
if (name.equals_ignoring_ascii_case("long"sv)) {
@ -252,6 +254,9 @@ NonnullRefPtr<Type const> Parser::parse_type()
StringBuilder builder;
if (unsigned_)
builder.append("unsigned "sv);
if (unrestricted)
builder.append("unrestricted "sv);
builder.append(name);
if (nullable) {

View File

@ -125,7 +125,7 @@ public:
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() || m_name.is_one_of("float", "unrestricted float", "double", "unrestricted double")); }
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"); }
@ -138,6 +138,10 @@ public:
bool is_json(Interface 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;

View File

@ -70,6 +70,7 @@
M(InvalidLength, "Invalid {} length") \
M(InvalidOrAmbiguousExportEntry, "Invalid or ambiguous export entry '{}'") \
M(InvalidPrecision, "Precision must be an integer no less than 1, and no greater than 100") \
M(InvalidRestrictedFloatingPointParameter, "Expected {} to be a finite floating-point number") \
M(InvalidTimeValue, "Invalid time value") \
M(InvalidRadix, "Radix must be an integer no less than 2, and no greater than 36") \
M(IsNotA, "{} is not a {}") \