1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 17:37:34 +00:00

LibJS: Implement Temporal.Instant.prototype.add()

This commit is contained in:
Linus Groh 2021-08-07 00:31:08 +01:00
parent 8ffad70504
commit b38f1fb071
8 changed files with 173 additions and 3 deletions

View file

@ -118,7 +118,7 @@ TemporalDuration to_temporal_duration_record(GlobalObject& global_object, Object
// e. If floor(val) ≠ val, then
if (floor(value.as_double()) != value.as_double()) {
// i. Throw a RangeError exception.
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidDurationPropertyValue, property.as_string(), value.to_string_without_side_effects());
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidDurationPropertyValueNonIntegral, property.as_string(), value.to_string_without_side_effects());
return {};
}
@ -266,4 +266,56 @@ Duration* create_temporal_duration(GlobalObject& global_object, double years, do
return object;
}
// 7.5.19 ToLimitedTemporalDuration ( temporalDurationLike, disallowedFields ),https://tc39.es/proposal-temporal/#sec-temporal-tolimitedtemporalduration
Optional<TemporalDuration> to_limited_temporal_duration(GlobalObject& global_object, Value temporal_duration_like, Vector<StringView> const& disallowed_fields)
{
auto& vm = global_object.vm();
Optional<TemporalDuration> duration;
// 1. If Type(temporalDurationLike) is not Object, then
if (!temporal_duration_like.is_object()) {
// a. Let str be ? ToString(temporalDurationLike).
auto str = temporal_duration_like.to_string(global_object);
if (vm.exception())
return {};
// b. Let duration be ? ParseTemporalDurationString(str).
duration = parse_temporal_duration_string(global_object, str);
if (vm.exception())
return {};
}
// 2. Else,
else {
// a. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
duration = to_temporal_duration_record(global_object, temporal_duration_like.as_object());
if (vm.exception())
return {};
}
// 3. If ! IsValidDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]) is false, throw a RangeError exception.
if (!is_valid_duration(duration->years, duration->months, duration->weeks, duration->days, duration->hours, duration->minutes, duration->seconds, duration->milliseconds, duration->microseconds, duration->nanoseconds)) {
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidDuration);
return {};
}
// 4. For each row of Table 7, except the header row, in table order, do
for (auto& [internal_slot, property] : temporal_duration_like_properties<TemporalDuration, double>(vm)) {
// a. Let prop be the Property value of the current row.
// b. Let value be duration's internal slot whose name is the Internal Slot value of the current row.
auto value = (*duration).*internal_slot;
// If value is not 0 and disallowedFields contains prop, then
if (value != 0 && disallowed_fields.contains_slow(property.as_string())) {
// i. Throw a RangeError exception.
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidDurationPropertyValueNonZero, property.as_string(), value);
return {};
}
}
// 5. Return duration.
return duration;
}
}

View file

@ -102,5 +102,6 @@ i8 duration_sign(double years, double months, double weeks, double days, double
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
PartialDuration to_partial_duration(GlobalObject&, Value temporal_duration_like);
Duration* create_temporal_duration(GlobalObject&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, FunctionObject* new_target = nullptr);
Optional<TemporalDuration> to_limited_temporal_duration(GlobalObject&, Value temporal_duration_like, Vector<StringView> const& disallowed_fields);
}

View file

@ -160,6 +160,35 @@ i32 compare_epoch_nanoseconds(BigInt const& epoch_nanoseconds_one, BigInt const&
return 0;
}
// 8.5.6 AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-addinstant
BigInt* add_instant(GlobalObject& global_object, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
{
auto& vm = global_object.vm();
// 1. Assert: hours, minutes, seconds, milliseconds, microseconds, and nanoseconds are integer Number values.
VERIFY(hours == trunc(hours) && minutes == trunc(minutes) && seconds == trunc(seconds) && milliseconds == trunc(milliseconds) && microseconds == trunc(microseconds) && nanoseconds == trunc(nanoseconds));
// 2. Let result be epochNanoseconds + (nanoseconds) + (microseconds) × 1000 + (milliseconds) × 10^6 + (seconds) × 10^9 + (minutes) × 60 × 10^9 + (hours) × 3600 × 10^9.
// FIXME: Pretty sure i64's are not sufficient for the extreme cases.
auto* result = js_bigint(vm,
epoch_nanoseconds.big_integer()
.plus(Crypto::SignedBigInteger::create_from((i64)nanoseconds))
.plus(Crypto::SignedBigInteger::create_from((i64)microseconds).multiplied_by(Crypto::SignedBigInteger { 1'000 }))
.plus(Crypto::SignedBigInteger::create_from((i64)milliseconds).multiplied_by(Crypto::SignedBigInteger { 1'000'000 }))
.plus(Crypto::SignedBigInteger::create_from((i64)seconds).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
.plus(Crypto::SignedBigInteger::create_from((i64)minutes).multiplied_by(Crypto::SignedBigInteger { 60 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
.plus(Crypto::SignedBigInteger::create_from((i64)hours).multiplied_by(Crypto::SignedBigInteger { 3600 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 })));
// If ! IsValidEpochNanoseconds(result) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(*result)) {
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidEpochNanoseconds);
return {};
}
// 4. Return result.
return result;
}
// 8.5.8 RoundTemporalInstant ( ns, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
BigInt* round_temporal_instant(GlobalObject& global_object, BigInt const& nanoseconds, u64 increment, String const& unit, String const& rounding_mode)
{

View file

@ -39,6 +39,7 @@ Instant* create_temporal_instant(GlobalObject&, BigInt& nanoseconds, FunctionObj
Instant* to_temporal_instant(GlobalObject&, Value item);
BigInt* parse_temporal_instant(GlobalObject&, String const& iso_string);
i32 compare_epoch_nanoseconds(BigInt const&, BigInt const&);
BigInt* add_instant(GlobalObject&, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
BigInt* round_temporal_instant(GlobalObject&, BigInt const& nanoseconds, u64 increment, String const& unit, String const& rounding_mode);
}

View file

@ -8,6 +8,7 @@
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
@ -34,6 +35,7 @@ void InstantPrototype::initialize(GlobalObject& global_object)
define_native_accessor(vm.names.epochNanoseconds, epoch_nanoseconds_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(vm.names.add, add, 1, attr);
define_native_function(vm.names.round, round, 1, attr);
define_native_function(vm.names.equals, equals, 1, attr);
define_native_function(vm.names.valueOf, value_of, 0, attr);
@ -125,6 +127,31 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_nanoseconds_getter)
return &ns;
}
// 8.3.7 Temporal.Instant.prototype.add ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.add
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::add)
{
auto temporal_duration_like = vm.argument(0);
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto* instant = typed_this(global_object);
if (vm.exception())
return {};
// 3. Let duration be ? ToLimitedTemporalDuration(temporalDurationLike, « "years", "months", "weeks", "days" »).
auto duration = to_limited_temporal_duration(global_object, temporal_duration_like, { "years"sv, "months"sv, "weeks"sv, "days"sv });
if (vm.exception())
return {};
// 4. Let ns be ? AddInstant(instant.[[Nanoseconds]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]).
auto* ns = add_instant(global_object, instant->nanoseconds(), duration->hours, duration->minutes, duration->seconds, duration->milliseconds, duration->microseconds, duration->nanoseconds);
if (vm.exception())
return {};
// 5. Return ! CreateTemporalInstant(ns).
return create_temporal_instant(global_object, *ns);
}
// 8.3.11 Temporal.Instant.prototype.round ( options ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round)
{

View file

@ -23,7 +23,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(epoch_milliseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_microseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_nanoseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(round);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(value_of);