From 11371acfafd0c8ae1b3535b8acbdcef2dd748374 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Wed, 27 Dec 2023 21:00:56 +1300 Subject: [PATCH] LibWeb/WebIDL: Implement ConvertToInt and IntegerPart AOs These are used when converting JS::Values to integers in IDL, as opposed to our current AD-HOC solution. --- .../BindingsGenerator/IDLGenerators.cpp | 1 + .../LibWeb/WebIDL/AbstractOperations.cpp | 113 ++++++++++++++++++ .../LibWeb/WebIDL/AbstractOperations.h | 18 +++ 3 files changed, 132 insertions(+) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index a3f62d1df5..68a10c3753 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -4368,6 +4368,7 @@ void generate_global_mixin_implementation(IDL::Interface const& interface, Strin #include #include #include +#include )~~~"); diff --git a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp index 0da1d8d1d3..2a7ba48e45 100644 --- a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp @@ -1,22 +1,26 @@ /* * Copyright (c) 2021-2023, Luke Wilde * Copyright (c) 2021-2022, Linus Groh + * Copyright (c) 2023, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include namespace Web::WebIDL { @@ -366,4 +370,113 @@ JS::ThrowCompletionOr is_named_property_exposed_on_object(Variant +JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value value, EnforceRange enforce_range, Clamp clamp) +{ + double upper_bound = 0; + double lower_bound = 0; + + // 1. If bitLength is 64, then: + if constexpr (sizeof(T) == 4) { + // 1. Let upperBound be 2^(53) − 1 + upper_bound = JS::MAX_ARRAY_LIKE_INDEX; + + // 2. If signedness is "unsigned", then let lowerBound be 0. + if constexpr (IsUnsigned) { + lower_bound = 0; + } + // 3. Otherwise let lowerBound be −2^(53) + 1. + else { + lower_bound = -JS::MAX_ARRAY_LIKE_INDEX; + } + + // Note: this ensures long long types associated with [EnforceRange] or [Clamp] extended attributes are representable in ECMAScript’s Number type as unambiguous integers. + } else { + // 2. Otherwise, if signedness is "unsigned", then: + // 1. Let lowerBound be 0. + // 2. Let upperBound be 2^(bitLength) − 1. + // 3. Otherwise: + // 1. Let lowerBound be -2^(bitLength − 1). + // 2. Let upperBound be 2^(bitLength − 1) − 1. + lower_bound = NumericLimits::min(); + upper_bound = NumericLimits::max(); + } + + // 4. Let x be ? ToNumber(V). + auto x = TRY(value.to_number(vm)).as_double(); + + // 5. If x is −0, then set x to +0. + if (x == -0.) + x = 0.; + + // 6. If the conversion is to an IDL type associated with the [EnforceRange] extended attribute, then: + if (enforce_range == EnforceRange::Yes) { + // 1. If x is NaN, +∞, or −∞, then throw a TypeError. + if (isnan(x) || isinf(x)) + return vm.throw_completion(JS::ErrorType::NumberIsNaNOrInfinity); + + // 2. Set x to IntegerPart(x). + x = integer_part(x); + + // 3. If x < lowerBound or x > upperBound, then throw a TypeError. + if (x < lower_bound || x > upper_bound) + return vm.throw_completion(MUST(String::formatted("Number '{}' is outside of allowed range of {} to {}", x, lower_bound, upper_bound))); + + // 4. Return x. + return x; + } + + // 7. If x is not NaN and the conversion is to an IDL type associated with the [Clamp] extended attribute, then: + if (clamp == Clamp::Yes && !isnan(x)) { + // 1. Set x to min(max(x, lowerBound), upperBound). + x = min(max(x, lower_bound), upper_bound); + + // 2. Round x to the nearest integer, choosing the even integer if it lies halfway between two, and choosing +0 rather than −0. + // 3. Return x. + return round(x); + } + + // 8. If x is NaN, +0, +∞, or −∞, then return +0. + if (isnan(x) || x == 0.0 || isinf(x)) + return 0; + + // 9. Set x to IntegerPart(x). + x = integer_part(x); + + // 10. Set x to x modulo 2^bitLength. + auto constexpr two_pow_bitlength = NumericLimits>::max() + 1.0; + x = JS::modulo(x, two_pow_bitlength); + + // 11. If signedness is "signed" and x ≥ 2^(bitLength − 1), then return x − 2^(bitLength). + if (IsSigned && x > NumericLimits::max()) + return x - two_pow_bitlength; + + // 12. Otherwise, return x. + return x; +} + +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); +template JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange, Clamp); + } diff --git a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h index 650eeedd76..26f4408b6e 100644 --- a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h +++ b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2021-2023, Luke Wilde * Copyright (c) 2021-2022, Linus Groh + * Copyright (c) 2023, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -63,4 +64,21 @@ JS::Completion construct(WebIDL::CallbackType& callback, Args&&... args) JS::ThrowCompletionOr is_named_property_exposed_on_object(Variant const& variant, JS::PropertyKey const& property_key); +// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart +double integer_part(double n); + +enum class EnforceRange { + Yes, + No, +}; + +enum class Clamp { + Yes, + No, +}; + +// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint +template +JS::ThrowCompletionOr convert_to_int(JS::VM& vm, JS::Value, EnforceRange enforce_range = EnforceRange::No, Clamp clamp = Clamp::No); + }