mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 04:25:13 +00:00
LibJS: Migrate Temporal updates to ECMA-262 AOs to the main AO file
These are going to be included in the ECMA-262 AOs once Temporal reaches stage 4. There's no need to keep them in the Temporal namespace. Some upcoming Temporal editorial changes will get awkward without this patch.
This commit is contained in:
parent
ea52952774
commit
a8d6e5c3db
Notes:
github-actions[bot]
2025-03-01 13:50:43 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/a8d6e5c3db2 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3734
13 changed files with 183 additions and 218 deletions
|
@ -36,6 +36,7 @@
|
|||
#include <LibJS/Runtime/Reference.h>
|
||||
#include <LibJS/Runtime/StringPrototype.h>
|
||||
#include <LibJS/Runtime/SuppressedError.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
|
||||
namespace JS {
|
||||
|
@ -1810,4 +1811,127 @@ ThrowCompletionOr<Value> perform_import_call(VM& vm, Value specifier, Value opti
|
|||
return Value { promise_capability->promise() };
|
||||
}
|
||||
|
||||
// 14.5.2.1 GetOptionsObject ( options ), https://tc39.es/proposal-temporal/#sec-getoptionsobject
|
||||
ThrowCompletionOr<GC::Ref<Object>> get_options_object(VM& vm, Value options)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. If options is undefined, then
|
||||
if (options.is_undefined()) {
|
||||
// a. Return OrdinaryObjectCreate(null).
|
||||
return Object::create(realm, nullptr);
|
||||
}
|
||||
|
||||
// 2. If options is an Object, then
|
||||
if (options.is_object()) {
|
||||
// a. Return options.
|
||||
return options.as_object();
|
||||
}
|
||||
|
||||
// 3. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, "Options");
|
||||
}
|
||||
|
||||
// 14.5.2.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_)
|
||||
{
|
||||
VERIFY(property.is_string());
|
||||
|
||||
// 1. Let value be ? Get(options, property).
|
||||
auto value = TRY(options.get(property));
|
||||
|
||||
// 2. If value is undefined, then
|
||||
if (value.is_undefined()) {
|
||||
// a. If default is REQUIRED, throw a RangeError exception.
|
||||
if (default_.has<Required>())
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, "undefined"sv, property.as_string());
|
||||
|
||||
// b. Return default.
|
||||
return default_.visit(
|
||||
[](Required) -> Value { VERIFY_NOT_REACHED(); },
|
||||
[](Empty) -> Value { return js_undefined(); },
|
||||
[](bool default_) -> Value { return Value { default_ }; },
|
||||
[](double default_) -> Value { return Value { default_ }; },
|
||||
[&](StringView default_) -> Value { return PrimitiveString::create(vm, default_); });
|
||||
}
|
||||
|
||||
// 3. If type is BOOLEAN, then
|
||||
if (type == OptionType::Boolean) {
|
||||
// a. Set value to ToBoolean(value).
|
||||
value = Value { value.to_boolean() };
|
||||
}
|
||||
// 4. Else,
|
||||
else {
|
||||
// a. Assert: type is STRING.
|
||||
VERIFY(type == OptionType::String);
|
||||
|
||||
// b. Set value to ? ToString(value).
|
||||
value = TRY(value.to_primitive_string(vm));
|
||||
}
|
||||
|
||||
// 5. If values is not EMPTY and values does not contain value, throw a RangeError exception.
|
||||
if (!values.is_empty()) {
|
||||
// NOTE: Every location in the spec that invokes GetOption with type=boolean also has values=undefined.
|
||||
VERIFY(value.is_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());
|
||||
}
|
||||
|
||||
// 6. Return value.
|
||||
return value;
|
||||
}
|
||||
|
||||
// 14.5.2.3 GetRoundingModeOption ( options, fallback ), https://tc39.es/proposal-temporal/#sec-temporal-getroundingmodeoption
|
||||
ThrowCompletionOr<RoundingMode> get_rounding_mode_option(VM& vm, Object const& options, RoundingMode fallback)
|
||||
{
|
||||
// 1. Let allowedStrings be the List of Strings from the "String Identifier" column of Table 26.
|
||||
static constexpr auto allowed_strings = to_array({ "ceil"sv, "floor"sv, "expand"sv, "trunc"sv, "halfCeil"sv, "halfFloor"sv, "halfExpand"sv, "halfTrunc"sv, "halfEven"sv });
|
||||
|
||||
// 2. Let stringFallback be the value from the "String Identifier" column of the row with fallback in its "Rounding Mode" column.
|
||||
auto string_fallback = allowed_strings[to_underlying(fallback)];
|
||||
|
||||
// 3. Let stringValue be ? GetOption(options, "roundingMode", STRING, allowedStrings, stringFallback).
|
||||
auto string_value = TRY(get_option(vm, options, vm.names.roundingMode, OptionType::String, allowed_strings, string_fallback));
|
||||
|
||||
// 4. Return the value from the "Rounding Mode" column of the row with stringValue in its "String Identifier" column.
|
||||
return static_cast<RoundingMode>(allowed_strings.first_index_of(string_value.as_string().utf8_string_view()).value());
|
||||
}
|
||||
|
||||
// 14.5.2.4 GetRoundingIncrementOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption
|
||||
ThrowCompletionOr<u64> get_rounding_increment_option(VM& vm, Object const& options)
|
||||
{
|
||||
// 1. Let value be ? Get(options, "roundingIncrement").
|
||||
auto value = TRY(options.get(vm.names.roundingIncrement));
|
||||
|
||||
// 2. If value is undefined, return 1𝔽.
|
||||
if (value.is_undefined())
|
||||
return 1;
|
||||
|
||||
// 3. Let integerIncrement be ? ToIntegerWithTruncation(value).
|
||||
auto integer_increment = TRY(Temporal::to_integer_with_truncation(vm, value, ErrorType::OptionIsNotValidValue, value, "roundingIncrement"sv));
|
||||
|
||||
// 4. If integerIncrement < 1 or integerIncrement > 10**9, throw a RangeError exception.
|
||||
if (integer_increment < 1 || integer_increment > 1'000'000'000u)
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, value, "roundingIncrement");
|
||||
|
||||
// 5. Return integerIncrement.
|
||||
return static_cast<u64>(integer_increment);
|
||||
}
|
||||
|
||||
// AD-HOC
|
||||
// FIXME: We should add a generic floor() method to our BigInt classes. But for now, since we know we are only dividing
|
||||
// by powers of 10, we can implement a very situationally specific method to compute the floor of a division.
|
||||
Crypto::SignedBigInteger big_floor(Crypto::SignedBigInteger const& numerator, Crypto::UnsignedBigInteger const& denominator)
|
||||
{
|
||||
auto result = numerator.divided_by(denominator);
|
||||
|
||||
if (result.remainder.is_zero())
|
||||
return result.quotient;
|
||||
if (!result.quotient.is_negative() && result.remainder.is_positive())
|
||||
return result.quotient;
|
||||
|
||||
return result.quotient.minus(Crypto::SignedBigInteger { 1 });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -163,35 +163,6 @@ ThrowCompletionOr<GC::Ref<T>> ordinary_create_from_constructor(VM& vm, FunctionO
|
|||
return realm.create<T>(forward<Args>(args)..., *prototype);
|
||||
}
|
||||
|
||||
// 14.1 MergeLists ( a, b ), https://tc39.es/proposal-temporal/#sec-temporal-mergelists
|
||||
template<typename T>
|
||||
Vector<T> merge_lists(Vector<T> const& a, Vector<T> const& b)
|
||||
{
|
||||
// 1. Let merged be a new empty List.
|
||||
Vector<T> merged;
|
||||
|
||||
// 2. For each element element of a, do
|
||||
for (auto const& element : a) {
|
||||
// a. If merged does not contain element, then
|
||||
if (!merged.contains_slow(element)) {
|
||||
// i. Append element to merged.
|
||||
merged.append(element);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. For each element element of b, do
|
||||
for (auto const& element : b) {
|
||||
// a. If merged does not contain element, then
|
||||
if (!merged.contains_slow(element)) {
|
||||
// i. Append element to merged.
|
||||
merged.append(element);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Return merged.
|
||||
return merged;
|
||||
}
|
||||
|
||||
// 7.3.35 AddValueToKeyedGroup ( groups, key, value ), https://tc39.es/ecma262/#sec-add-value-to-keyed-group
|
||||
template<typename GroupsType, typename KeyType>
|
||||
void add_value_to_keyed_group(VM& vm, GroupsType& groups, KeyType key, Value value)
|
||||
|
@ -348,4 +319,45 @@ auto remainder(Crypto::BigInteger auto const& x, Crypto::BigInteger auto const&
|
|||
return x.divided_by(y).remainder;
|
||||
}
|
||||
|
||||
// 14.3 The Year-Week Record Specification Type, https://tc39.es/proposal-temporal/#sec-year-week-record-specification-type
|
||||
struct YearWeek {
|
||||
Optional<u8> week;
|
||||
Optional<i32> year;
|
||||
};
|
||||
|
||||
enum class OptionType {
|
||||
Boolean,
|
||||
String,
|
||||
};
|
||||
|
||||
struct Required { };
|
||||
using OptionDefault = Variant<Required, Empty, bool, StringView, double>;
|
||||
|
||||
ThrowCompletionOr<GC::Ref<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&);
|
||||
|
||||
template<size_t Size>
|
||||
ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_)
|
||||
{
|
||||
return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_);
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-temporal/#table-temporal-rounding-modes
|
||||
enum class RoundingMode {
|
||||
Ceil,
|
||||
Floor,
|
||||
Expand,
|
||||
Trunc,
|
||||
HalfCeil,
|
||||
HalfFloor,
|
||||
HalfExpand,
|
||||
HalfTrunc,
|
||||
HalfEven,
|
||||
};
|
||||
|
||||
ThrowCompletionOr<RoundingMode> get_rounding_mode_option(VM&, Object const& options, RoundingMode fallback);
|
||||
ThrowCompletionOr<u64> get_rounding_increment_option(VM&, Object const& options);
|
||||
|
||||
Crypto::SignedBigInteger big_floor(Crypto::SignedBigInteger const& numerator, Crypto::UnsignedBigInteger const& denominator);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -9,6 +9,7 @@
|
|||
#include <AK/Find.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Intl/AbstractOperations.h>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -13,7 +13,6 @@
|
|||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibUnicode/Locale.h>
|
||||
|
||||
|
@ -72,9 +71,4 @@ ThrowCompletionOr<StringOrBoolean> get_boolean_or_string_number_format_option(VM
|
|||
return get_boolean_or_string_number_format_option(vm, options, property, ReadonlySpan<StringView> { string_values }, move(fallback));
|
||||
}
|
||||
|
||||
// NOTE: ECMA-402's GetOption is being removed in favor of a shared ECMA-262 GetOption in the Temporal proposal.
|
||||
// Until Temporal is merged into ECMA-262, our implementation lives in the Temporal-specific AO file & namespace.
|
||||
using Temporal::get_option;
|
||||
using Temporal::OptionType;
|
||||
|
||||
}
|
||||
|
|
|
@ -485,7 +485,7 @@ bool same_temporal_type(FormattableDateTime const& x, FormattableDateTime const&
|
|||
|
||||
static double to_epoch_milliseconds(Crypto::SignedBigInteger const& epoch_nanoseconds)
|
||||
{
|
||||
return Temporal::big_floor(epoch_nanoseconds, Temporal::NANOSECONDS_PER_MILLISECOND).to_double();
|
||||
return big_floor(epoch_nanoseconds, Temporal::NANOSECONDS_PER_MILLISECOND).to_double();
|
||||
}
|
||||
|
||||
// 15.9.15 HandleDateTimeTemporalDate ( dateTimeFormat, temporalDate ), https://tc39.es/proposal-temporal/#sec-temporal-handledatetimetemporaldate
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2021-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -10,7 +10,6 @@
|
|||
#include <LibJS/Runtime/Intl/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Intl/DisplayNames.h>
|
||||
#include <LibJS/Runtime/Intl/DisplayNamesConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibUnicode/Locale.h>
|
||||
|
||||
namespace JS::Intl {
|
||||
|
@ -64,7 +63,7 @@ ThrowCompletionOr<GC::Ref<Object>> DisplayNamesConstructor::construct(FunctionOb
|
|||
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "options"sv);
|
||||
|
||||
// 5. Set options to ? GetOptionsObject(options).
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(get_options_object(vm, options_value));
|
||||
|
||||
// 6. Let opt be a new Record.
|
||||
LocaleOptions opt {};
|
||||
|
|
|
@ -60,7 +60,7 @@ ThrowCompletionOr<GC::Ref<Object>> DurationFormatConstructor::construct(Function
|
|||
auto requested_locales = TRY(canonicalize_locale_list(vm, locales));
|
||||
|
||||
// 4. Let options be ? GetOptionsObject(options).
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(get_options_object(vm, options_value));
|
||||
|
||||
// 5. Let matcher be ? GetOption(options, "localeMatcher", STRING, « "lookup", "best fit" », "best fit").
|
||||
auto matcher = TRY(get_option(vm, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv));
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <LibJS/Runtime/Intl/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Intl/ListFormat.h>
|
||||
#include <LibJS/Runtime/Intl/ListFormatConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
|
||||
namespace JS::Intl {
|
||||
|
||||
|
@ -59,7 +58,7 @@ ThrowCompletionOr<GC::Ref<Object>> ListFormatConstructor::construct(FunctionObje
|
|||
auto requested_locales = TRY(canonicalize_locale_list(vm, locale_value));
|
||||
|
||||
// 4. Set options to ? GetOptionsObject(options).
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(get_options_object(vm, options_value));
|
||||
|
||||
// 5. Let opt be a new Record.
|
||||
LocaleOptions opt {};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include <LibJS/Runtime/Intl/PluralRules.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
|
||||
namespace JS::Intl {
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <LibJS/Runtime/Intl/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Intl/Segmenter.h>
|
||||
#include <LibJS/Runtime/Intl/SegmenterConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
|
||||
namespace JS::Intl {
|
||||
|
||||
|
@ -60,7 +59,7 @@ ThrowCompletionOr<GC::Ref<Object>> SegmenterConstructor::construct(FunctionObjec
|
|||
auto requested_locales = TRY(canonicalize_locale_list(vm, locales));
|
||||
|
||||
// 5. Set options to ? GetOptionsObject(options).
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(get_options_object(vm, options_value));
|
||||
|
||||
// 6. Let opt be a new Record.
|
||||
LocaleOptions opt {};
|
||||
|
|
|
@ -2,13 +2,12 @@
|
|||
* Copyright (c) 2021-2022, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
* Copyright (c) 2024-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCrypto/BigFraction/BigFraction.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/PropertyKey.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
|
@ -1799,127 +1798,4 @@ ThrowCompletionOr<DifferenceSettings> get_difference_settings(VM& vm, DurationOp
|
|||
return DifferenceSettings { .smallest_unit = smallest_unit_value, .largest_unit = largest_unit_value, .rounding_mode = rounding_mode, .rounding_increment = rounding_increment };
|
||||
}
|
||||
|
||||
// 14.4.1.1 GetOptionsObject ( options ), https://tc39.es/proposal-temporal/#sec-getoptionsobject
|
||||
ThrowCompletionOr<GC::Ref<Object>> get_options_object(VM& vm, Value options)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. If options is undefined, then
|
||||
if (options.is_undefined()) {
|
||||
// a. Return OrdinaryObjectCreate(null).
|
||||
return Object::create(realm, nullptr);
|
||||
}
|
||||
|
||||
// 2. If options is an Object, then
|
||||
if (options.is_object()) {
|
||||
// a. Return options.
|
||||
return options.as_object();
|
||||
}
|
||||
|
||||
// 3. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, "Options");
|
||||
}
|
||||
|
||||
// 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_)
|
||||
{
|
||||
VERIFY(property.is_string());
|
||||
|
||||
// 1. Let value be ? Get(options, property).
|
||||
auto value = TRY(options.get(property));
|
||||
|
||||
// 2. If value is undefined, then
|
||||
if (value.is_undefined()) {
|
||||
// a. If default is REQUIRED, throw a RangeError exception.
|
||||
if (default_.has<Required>())
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, "undefined"sv, property.as_string());
|
||||
|
||||
// b. Return default.
|
||||
return default_.visit(
|
||||
[](Required) -> Value { VERIFY_NOT_REACHED(); },
|
||||
[](Empty) -> Value { return js_undefined(); },
|
||||
[](bool default_) -> Value { return Value { default_ }; },
|
||||
[](double default_) -> Value { return Value { default_ }; },
|
||||
[&](StringView default_) -> Value { return PrimitiveString::create(vm, default_); });
|
||||
}
|
||||
|
||||
// 3. If type is BOOLEAN, then
|
||||
if (type == OptionType::Boolean) {
|
||||
// a. Set value to ToBoolean(value).
|
||||
value = Value { value.to_boolean() };
|
||||
}
|
||||
// 4. Else,
|
||||
else {
|
||||
// a. Assert: type is STRING.
|
||||
VERIFY(type == OptionType::String);
|
||||
|
||||
// b. Set value to ? ToString(value).
|
||||
value = TRY(value.to_primitive_string(vm));
|
||||
}
|
||||
|
||||
// 5. If values is not EMPTY and values does not contain value, throw a RangeError exception.
|
||||
if (!values.is_empty()) {
|
||||
// NOTE: Every location in the spec that invokes GetOption with type=boolean also has values=undefined.
|
||||
VERIFY(value.is_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());
|
||||
}
|
||||
|
||||
// 6. Return value.
|
||||
return value;
|
||||
}
|
||||
|
||||
// 14.4.1.3 GetRoundingModeOption ( options, fallback ), https://tc39.es/proposal-temporal/#sec-temporal-getroundingmodeoption
|
||||
ThrowCompletionOr<RoundingMode> get_rounding_mode_option(VM& vm, Object const& options, RoundingMode fallback)
|
||||
{
|
||||
// 1. Let allowedStrings be the List of Strings from the "String Identifier" column of Table 26.
|
||||
static constexpr auto allowed_strings = to_array({ "ceil"sv, "floor"sv, "expand"sv, "trunc"sv, "halfCeil"sv, "halfFloor"sv, "halfExpand"sv, "halfTrunc"sv, "halfEven"sv });
|
||||
|
||||
// 2. Let stringFallback be the value from the "String Identifier" column of the row with fallback in its "Rounding Mode" column.
|
||||
auto string_fallback = allowed_strings[to_underlying(fallback)];
|
||||
|
||||
// 3. Let stringValue be ? GetOption(options, "roundingMode", STRING, allowedStrings, stringFallback).
|
||||
auto string_value = TRY(get_option(vm, options, vm.names.roundingMode, OptionType::String, allowed_strings, string_fallback));
|
||||
|
||||
// 4. Return the value from the "Rounding Mode" column of the row with stringValue in its "String Identifier" column.
|
||||
return static_cast<RoundingMode>(allowed_strings.first_index_of(string_value.as_string().utf8_string_view()).value());
|
||||
}
|
||||
|
||||
// 14.4.1.4 GetRoundingIncrementOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-getroundingincrementoption
|
||||
ThrowCompletionOr<u64> get_rounding_increment_option(VM& vm, Object const& options)
|
||||
{
|
||||
// 1. Let value be ? Get(options, "roundingIncrement").
|
||||
auto value = TRY(options.get(vm.names.roundingIncrement));
|
||||
|
||||
// 2. If value is undefined, return 1𝔽.
|
||||
if (value.is_undefined())
|
||||
return 1;
|
||||
|
||||
// 3. Let integerIncrement be ? ToIntegerWithTruncation(value).
|
||||
auto integer_increment = TRY(to_integer_with_truncation(vm, value, ErrorType::OptionIsNotValidValue, value, "roundingIncrement"sv));
|
||||
|
||||
// 4. If integerIncrement < 1 or integerIncrement > 10**9, throw a RangeError exception.
|
||||
if (integer_increment < 1 || integer_increment > 1'000'000'000u)
|
||||
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, value, "roundingIncrement");
|
||||
|
||||
// 5. Return integerIncrement.
|
||||
return static_cast<u64>(integer_increment);
|
||||
}
|
||||
|
||||
// AD-HOC
|
||||
// FIXME: We should add a generic floor() method to our BigInt classes. But for now, since we know we are only dividing
|
||||
// by powers of 10, we can implement a very situationally specific method to compute the floor of a division.
|
||||
Crypto::SignedBigInteger big_floor(Crypto::SignedBigInteger const& numerator, Crypto::UnsignedBigInteger const& denominator)
|
||||
{
|
||||
auto result = numerator.divided_by(denominator);
|
||||
|
||||
if (result.remainder.is_zero())
|
||||
return result.quotient;
|
||||
if (!result.quotient.is_negative() && result.remainder.is_positive())
|
||||
return result.quotient;
|
||||
|
||||
return result.quotient.minus(Crypto::SignedBigInteger { 1 });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
* Copyright (c) 2024-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -13,6 +13,7 @@
|
|||
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
#include <LibGC/Ptr.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Temporal/ISO8601.h>
|
||||
#include <LibJS/Runtime/Temporal/ISORecords.h>
|
||||
|
@ -113,19 +114,6 @@ enum class UnitGroup {
|
|||
DateTime,
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
||||
enum class RoundingMode {
|
||||
Ceil,
|
||||
Floor,
|
||||
Expand,
|
||||
Trunc,
|
||||
HalfCeil,
|
||||
HalfFloor,
|
||||
HalfExpand,
|
||||
HalfTrunc,
|
||||
HalfEven,
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
|
||||
enum class UnsignedRoundingMode {
|
||||
HalfEven,
|
||||
|
@ -142,7 +130,6 @@ enum class Sign {
|
|||
};
|
||||
|
||||
struct Auto { };
|
||||
struct Required { };
|
||||
struct Unset { };
|
||||
using Precision = Variant<Auto, u8>;
|
||||
using RoundingIncrement = Variant<Unset, u64>;
|
||||
|
@ -271,31 +258,4 @@ ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorTy
|
|||
return number.as_double();
|
||||
}
|
||||
|
||||
// 14.2 The Year-Week Record Specification Type, https://tc39.es/proposal-temporal/#sec-year-week-record-specification-type
|
||||
struct YearWeek {
|
||||
Optional<u8> week;
|
||||
Optional<i32> year;
|
||||
};
|
||||
|
||||
enum class OptionType {
|
||||
Boolean,
|
||||
String,
|
||||
};
|
||||
|
||||
using OptionDefault = Variant<Required, Empty, bool, StringView, double>;
|
||||
|
||||
ThrowCompletionOr<GC::Ref<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&);
|
||||
|
||||
template<size_t Size>
|
||||
ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_)
|
||||
{
|
||||
return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_);
|
||||
}
|
||||
|
||||
ThrowCompletionOr<RoundingMode> get_rounding_mode_option(VM&, Object const& options, RoundingMode fallback);
|
||||
ThrowCompletionOr<u64> get_rounding_increment_option(VM&, Object const& options);
|
||||
|
||||
Crypto::SignedBigInteger big_floor(Crypto::SignedBigInteger const& numerator, Crypto::UnsignedBigInteger const& denominator);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||
* Copyright (c) 2024-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@
|
|||
#include <AK/Base64.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringUtils.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/Uint8Array.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
|
@ -87,7 +87,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::to_base64)
|
|||
auto typed_array = TRY(validate_uint8_array(vm));
|
||||
|
||||
// 3. Let opts be ? GetOptionsObject(options).
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(get_options_object(vm, options_value));
|
||||
|
||||
// 4. Let alphabet be ? Get(opts, "alphabet").
|
||||
// 5. If alphabet is undefined, set alphabet to "base64".
|
||||
|
@ -159,7 +159,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayConstructorHelpers::from_base64)
|
|||
return vm.throw_completion<TypeError>(ErrorType::NotAString, string_value);
|
||||
|
||||
// 2. Let opts be ? GetOptionsObject(options).
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(get_options_object(vm, options_value));
|
||||
|
||||
// 3. Let alphabet be ? Get(opts, "alphabet").
|
||||
// 4. If alphabet is undefined, set alphabet to "base64".
|
||||
|
@ -214,7 +214,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::set_from_base64)
|
|||
return vm.throw_completion<TypeError>(ErrorType::NotAString, string_value);
|
||||
|
||||
// 4. Let opts be ? GetOptionsObject(options).
|
||||
auto options = TRY(Temporal::get_options_object(vm, options_value));
|
||||
auto options = TRY(get_options_object(vm, options_value));
|
||||
|
||||
// 5. Let alphabet be ? Get(opts, "alphabet").
|
||||
// 6. If alphabet is undefined, set alphabet to "base64".
|
||||
|
|
Loading…
Add table
Reference in a new issue