mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
LibJS: Update spec steps for the few remaining Temporal AOs
This commit is contained in:
parent
d368fcadac
commit
c8d2404230
Notes:
github-actions[bot]
2024-11-21 00:06:44 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/c8d24042300 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2431 Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/shannonbooth ✅
4 changed files with 68 additions and 81 deletions
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||||
|
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -11,7 +12,7 @@
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
// 13.2 GetOptionsObject ( options ), https://tc39.es/proposal-temporal/#sec-getoptionsobject
|
// 14.4.1.1 GetOptionsObject ( options ), https://tc39.es/proposal-temporal/#sec-getoptionsobject
|
||||||
ThrowCompletionOr<Object*> get_options_object(VM& vm, Value options)
|
ThrowCompletionOr<Object*> get_options_object(VM& vm, Value options)
|
||||||
{
|
{
|
||||||
auto& realm = *vm.current_realm();
|
auto& realm = *vm.current_realm();
|
||||||
|
@ -22,7 +23,7 @@ ThrowCompletionOr<Object*> get_options_object(VM& vm, Value options)
|
||||||
return Object::create(realm, nullptr).ptr();
|
return Object::create(realm, nullptr).ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. If Type(options) is Object, then
|
// 2. If options is an Object, then
|
||||||
if (options.is_object()) {
|
if (options.is_object()) {
|
||||||
// a. Return options.
|
// a. Return options.
|
||||||
return &options.as_object();
|
return &options.as_object();
|
||||||
|
@ -32,7 +33,7 @@ ThrowCompletionOr<Object*> get_options_object(VM& vm, Value options)
|
||||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, "Options");
|
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, "Options");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.3 GetOption ( options, property, type, values, fallback ), https://tc39.es/proposal-temporal/#sec-getoption
|
// 14.4.1.2 GetOption ( options, property, type, values, default ), https://tc39.es/proposal-temporal/#sec-getoption
|
||||||
ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const& default_)
|
ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const& default_)
|
||||||
{
|
{
|
||||||
VERIFY(property.is_string());
|
VERIFY(property.is_string());
|
||||||
|
@ -42,51 +43,43 @@ ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey c
|
||||||
|
|
||||||
// 2. If value is undefined, then
|
// 2. If value is undefined, then
|
||||||
if (value.is_undefined()) {
|
if (value.is_undefined()) {
|
||||||
// a. If default is required, throw a RangeError exception.
|
// a. If default is REQUIRED, throw a RangeError exception.
|
||||||
if (default_.has<GetOptionRequired>())
|
if (default_.has<DefaultRequired>())
|
||||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, "undefined"sv, property.as_string());
|
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, "undefined"sv, property.as_string());
|
||||||
|
|
||||||
// b. Return default.
|
// b. Return default.
|
||||||
return default_.visit(
|
return default_.visit(
|
||||||
[](GetOptionRequired) -> ThrowCompletionOr<Value> { VERIFY_NOT_REACHED(); },
|
[](DefaultRequired) -> Value { VERIFY_NOT_REACHED(); },
|
||||||
[](Empty) -> ThrowCompletionOr<Value> { return js_undefined(); },
|
[](Empty) -> Value { return js_undefined(); },
|
||||||
[](bool b) -> ThrowCompletionOr<Value> { return Value(b); },
|
[](bool default_) -> Value { return Value { default_ }; },
|
||||||
[](double d) -> ThrowCompletionOr<Value> { return Value(d); },
|
[](double default_) -> Value { return Value { default_ }; },
|
||||||
[&vm](StringView s) -> ThrowCompletionOr<Value> { return PrimitiveString::create(vm, s); });
|
[&](StringView default_) -> Value { return PrimitiveString::create(vm, default_); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. If type is "boolean", then
|
// 3. If type is BOOLEAN, then
|
||||||
if (type == OptionType::Boolean) {
|
if (type == OptionType::Boolean) {
|
||||||
// a. Set value to ToBoolean(value).
|
// a. Set value to ToBoolean(value).
|
||||||
value = Value(value.to_boolean());
|
value = Value { value.to_boolean() };
|
||||||
}
|
}
|
||||||
// 6. Else if type is "number", then
|
// 4. Else,
|
||||||
else if (type == OptionType::Number) {
|
|
||||||
// a. Set value to ? ToNumber(value).
|
|
||||||
value = TRY(value.to_number(vm));
|
|
||||||
|
|
||||||
// b. If value is NaN, throw a RangeError exception.
|
|
||||||
if (value.is_nan())
|
|
||||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, vm.names.NaN.as_string(), property.as_string());
|
|
||||||
}
|
|
||||||
// 7. Else,
|
|
||||||
else {
|
else {
|
||||||
// a. Assert: type is "string".
|
// a. Assert: type is STRING.
|
||||||
VERIFY(type == OptionType::String);
|
VERIFY(type == OptionType::String);
|
||||||
|
|
||||||
// b. Set value to ? ToString(value).
|
// b. Set value to ? ToString(value).
|
||||||
value = TRY(value.to_primitive_string(vm));
|
value = TRY(value.to_primitive_string(vm));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. If values is not undefined and values does not contain an element equal to value, throw a RangeError exception.
|
// 5. If values is not EMPTY and values does not contain value, throw a RangeError exception.
|
||||||
if (!values.is_empty()) {
|
if (!values.is_empty()) {
|
||||||
// NOTE: Every location in the spec that invokes GetOption with type=boolean also has values=undefined.
|
// NOTE: Every location in the spec that invokes GetOption with type=boolean also has values=undefined.
|
||||||
VERIFY(value.is_string());
|
VERIFY(value.is_string());
|
||||||
|
|
||||||
if (auto value_string = value.as_string().utf8_string(); !values.contains_slow(value_string))
|
if (auto value_string = value.as_string().utf8_string(); !values.contains_slow(value_string))
|
||||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, value_string, property.as_string());
|
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, value_string, property.as_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9. Return value.
|
// 6. Return value.
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||||
|
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -15,14 +16,28 @@
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
|
// 13.39 ToIntegerIfIntegral ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerifintegral
|
||||||
|
template<typename... Args>
|
||||||
|
ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorType error_type, Args&&... args)
|
||||||
|
{
|
||||||
|
// 1. Let number be ? ToNumber(argument).
|
||||||
|
auto number = TRY(argument.to_number(vm));
|
||||||
|
|
||||||
|
// 2. If number is not an integral Number, throw a RangeError exception.
|
||||||
|
if (!number.is_integral_number())
|
||||||
|
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
|
||||||
|
|
||||||
|
// 3. Return ℝ(number).
|
||||||
|
return number.as_double();
|
||||||
|
}
|
||||||
|
|
||||||
enum class OptionType {
|
enum class OptionType {
|
||||||
Boolean,
|
Boolean,
|
||||||
String,
|
String,
|
||||||
Number
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GetOptionRequired { };
|
struct DefaultRequired { };
|
||||||
using OptionDefault = Variant<GetOptionRequired, Empty, bool, StringView, double>;
|
using OptionDefault = Variant<DefaultRequired, Empty, bool, StringView, double>;
|
||||||
|
|
||||||
ThrowCompletionOr<Object*> get_options_object(VM&, Value options);
|
ThrowCompletionOr<Object*> get_options_object(VM&, Value options);
|
||||||
ThrowCompletionOr<Value> get_option(VM&, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const&);
|
ThrowCompletionOr<Value> get_option(VM&, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const&);
|
||||||
|
@ -33,23 +48,4 @@ ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey c
|
||||||
return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_);
|
return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 13.41 ToIntegerIfIntegral ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerifintegral
|
|
||||||
template<typename... Args>
|
|
||||||
ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorType error_type, Args... args)
|
|
||||||
{
|
|
||||||
// 1. Let number be ? ToNumber(argument).
|
|
||||||
auto number = TRY(argument.to_number(vm));
|
|
||||||
|
|
||||||
// 2. If number is NaN, +0𝔽, or -0𝔽, return 0.
|
|
||||||
if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// 3. If IsIntegralNumber(number) is false, throw a RangeError exception.
|
|
||||||
if (!number.is_integral_number())
|
|
||||||
return vm.template throw_completion<RangeError>(error_type, args...);
|
|
||||||
|
|
||||||
// 4. Return ℝ(number).
|
|
||||||
return number.as_double();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||||
|
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -15,43 +16,36 @@
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
// 7.5.10 DurationSign ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-durationsign
|
// 7.5.16 IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration
|
||||||
i8 duration_sign(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
|
|
||||||
{
|
|
||||||
// 1. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
|
|
||||||
for (auto& v : { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }) {
|
|
||||||
// a. If v < 0, return -1.
|
|
||||||
if (v < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// b. If v > 0, return 1.
|
|
||||||
if (v > 0)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Return 0.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7.5.11 IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidduration
|
|
||||||
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
|
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
|
||||||
{
|
{
|
||||||
// 1. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
// 1. Let sign be 0.
|
||||||
auto sign = duration_sign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
|
auto sign = 0;
|
||||||
|
|
||||||
// 2. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
|
// 2. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
|
||||||
for (auto& v : { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }) {
|
for (auto value : { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }) {
|
||||||
// a. If 𝔽(v) is not finite, return false.
|
// a. If 𝔽(v) is not finite, return false.
|
||||||
if (!isfinite(v))
|
if (!isfinite(value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// b. If v < 0 and sign > 0, return false.
|
// b. If v < 0, then
|
||||||
if (v < 0 && sign > 0)
|
if (value < 0) {
|
||||||
return false;
|
// i. If sign > 0, return false.
|
||||||
|
if (sign > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
// c. If v > 0 and sign < 0, return false.
|
// ii. Set sign to -1.
|
||||||
if (v > 0 && sign < 0)
|
sign = -1;
|
||||||
return false;
|
}
|
||||||
|
// c. Else if v > 0, then
|
||||||
|
else if (value > 0) {
|
||||||
|
// i. If sign < 0, return false.
|
||||||
|
if (sign < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// ii. Set sign to 1.
|
||||||
|
sign = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. If abs(years) ≥ 2**32, return false.
|
// 3. If abs(years) ≥ 2**32, return false.
|
||||||
|
@ -66,8 +60,12 @@ bool is_valid_duration(double years, double months, double weeks, double days, d
|
||||||
if (AK::fabs(weeks) > NumericLimits<u32>::max())
|
if (AK::fabs(weeks) > NumericLimits<u32>::max())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 6. Let normalizedSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + ℝ(𝔽(milliseconds)) × 10**-3 + ℝ(𝔽(microseconds)) × 10**-6 + ℝ(𝔽(nanoseconds)) × 10**-9.
|
// 6. Let totalFractionalSeconds be days × 86,400 + hours × 3600 + minutes × 60 + seconds + ℝ(𝔽(milliseconds)) × 10**-3 + ℝ(𝔽(microseconds)) × 10**-6 + ℝ(𝔽(nanoseconds)) × 10**-9.
|
||||||
// 7. NOTE: The above step cannot be implemented directly using floating-point arithmetic. Multiplying by 10**-3, 10**-6, and 10**-9 respectively may be imprecise when milliseconds, microseconds, or nanoseconds is an unsafe integer. This multiplication can be implemented in C++ with an implementation of std::remquo() with sufficient bits in the quotient. String manipulation will also give an exact result, since the multiplication is by a power of 10.
|
// 7. NOTE: The above step cannot be implemented directly using floating-point arithmetic. Multiplying by 10**-3,
|
||||||
|
// 10**-6, and 10**-9 respectively may be imprecise when milliseconds, microseconds, or nanoseconds is an
|
||||||
|
// unsafe integer. This multiplication can be implemented in C++ with an implementation of std::remquo()
|
||||||
|
// with sufficient bits in the quotient. String manipulation will also give an exact result, since the
|
||||||
|
// multiplication is by a power of 10.
|
||||||
static Crypto::SignedBigInteger days_to_nanoseconds { 8.64e13 };
|
static Crypto::SignedBigInteger days_to_nanoseconds { 8.64e13 };
|
||||||
static Crypto::SignedBigInteger hours_to_nanoseconds { 3.6e12 };
|
static Crypto::SignedBigInteger hours_to_nanoseconds { 3.6e12 };
|
||||||
static Crypto::SignedBigInteger minutes_to_nanoseconds { 6e10 };
|
static Crypto::SignedBigInteger minutes_to_nanoseconds { 6e10 };
|
||||||
|
@ -84,7 +82,7 @@ bool is_valid_duration(double years, double months, double weeks, double days, d
|
||||||
normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { nanoseconds });
|
normalized_nanoseconds = normalized_nanoseconds.plus(Crypto::SignedBigInteger { nanoseconds });
|
||||||
|
|
||||||
// 8. If abs(normalizedSeconds) ≥ 2**53, return false.
|
// 8. If abs(normalizedSeconds) ≥ 2**53, return false.
|
||||||
static auto maximum_time = Crypto::SignedBigInteger { MAX_ARRAY_LIKE_INDEX }.plus(Crypto::SignedBigInteger { 1 }).multiplied_by(seconds_to_nanoseconds);
|
static auto maximum_time = Crypto::SignedBigInteger { MAX_ARRAY_LIKE_INDEX }.plus(1_bigint).multiplied_by(seconds_to_nanoseconds);
|
||||||
|
|
||||||
if (normalized_nanoseconds.is_negative())
|
if (normalized_nanoseconds.is_negative())
|
||||||
normalized_nanoseconds.negate();
|
normalized_nanoseconds.negate();
|
||||||
|
@ -92,7 +90,7 @@ bool is_valid_duration(double years, double months, double weeks, double days, d
|
||||||
if (normalized_nanoseconds >= maximum_time)
|
if (normalized_nanoseconds >= maximum_time)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 3. Return true.
|
// 9. Return true.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
|
||||||
|
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -11,7 +12,6 @@
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
i8 duration_sign(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
|
||||||
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue