From 3e221fbb2d6ee0b2177b7107881e079bb3145f8b Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Mon, 27 May 2024 08:04:58 -0700 Subject: [PATCH] IDLGenerators: Handle restricted/unrestricted floating point types --- .../BindingsGenerator/IDLGenerators.cpp | 33 ++++++++++++++++--- .../WebAnimations/misc/invalid-values.txt | 10 ++++++ .../Text/expected/geometry/domrect-create.txt | 24 +++++++++----- .../WebAnimations/misc/invalid-values.html | 28 ++++++++++++++++ .../Text/input/geometry/domrect-create.html | 12 +++++++ Userland/Libraries/LibIDL/IDLParser.cpp | 9 +++-- Userland/Libraries/LibIDL/Types.h | 6 +++- Userland/Libraries/LibJS/Runtime/ErrorTypes.h | 1 + 8 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/WebAnimations/misc/invalid-values.txt create mode 100644 Tests/LibWeb/Text/input/WebAnimations/misc/invalid-values.html diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index 5089940d6c..48cae944da 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -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::ErrorType::InvalidRestrictedFloatingPointParameter, "@parameter.name@"); + } + )~~~"); + } else { + scoped_generator.append(R"~~~( + if (isinf(@cpp_name@) || isnan(@cpp_name@)) { + return vm.throw_completion(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()); diff --git a/Tests/LibWeb/Text/expected/WebAnimations/misc/invalid-values.txt b/Tests/LibWeb/Text/expected/WebAnimations/misc/invalid-values.txt new file mode 100644 index 0000000000..ff0b13c212 --- /dev/null +++ b/Tests/LibWeb/Text/expected/WebAnimations/misc/invalid-values.txt @@ -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 diff --git a/Tests/LibWeb/Text/expected/geometry/domrect-create.txt b/Tests/LibWeb/Text/expected/geometry/domrect-create.txt index 9ec818a50a..68c4797381 100644 --- a/Tests/LibWeb/Text/expected/geometry/domrect-create.txt +++ b/Tests/LibWeb/Text/expected/geometry/domrect-create.txt @@ -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 diff --git a/Tests/LibWeb/Text/input/WebAnimations/misc/invalid-values.html b/Tests/LibWeb/Text/input/WebAnimations/misc/invalid-values.html new file mode 100644 index 0000000000..5dfb800330 --- /dev/null +++ b/Tests/LibWeb/Text/input/WebAnimations/misc/invalid-values.html @@ -0,0 +1,28 @@ + + + diff --git a/Tests/LibWeb/Text/input/geometry/domrect-create.html b/Tests/LibWeb/Text/input/geometry/domrect-create.html index 64c0a1cb09..fea01fe9c4 100644 --- a/Tests/LibWeb/Text/input/geometry/domrect-create.html +++ b/Tests/LibWeb/Text/input/geometry/domrect-create.html @@ -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; } }); diff --git a/Userland/Libraries/LibIDL/IDLParser.cpp b/Userland/Libraries/LibIDL/IDLParser.cpp index 41f8272dd4..83e68fa82b 100644 --- a/Userland/Libraries/LibIDL/IDLParser.cpp +++ b/Userland/Libraries/LibIDL/IDLParser.cpp @@ -225,10 +225,12 @@ NonnullRefPtr 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 Parser::parse_type() StringBuilder builder; if (unsigned_) builder.append("unsigned "sv); + if (unrestricted) + builder.append("unrestricted "sv); + builder.append(name); if (nullable) { diff --git a/Userland/Libraries/LibIDL/Types.h b/Userland/Libraries/LibIDL/Types.h index 8f15c3ebd4..b199c2e96c 100644 --- a/Userland/Libraries/LibIDL/Types.h +++ b/Userland/Libraries/LibIDL/Types.h @@ -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; diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index 28fd783c61..ac5a9209f7 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -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 {}") \