LibJS: Implement Temporal.Now
This commit is contained in:
parent
f1c3e3d71a
commit
f2c19f96f8
Notes:
github-actions[bot]
2024-11-25 12:33:55 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/f2c19f96f8e Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2557 Reviewed-by: https://github.com/shannonbooth ✅
12 changed files with 252 additions and 0 deletions
|
@ -214,6 +214,7 @@ set(SOURCES
|
|||
Runtime/Temporal/InstantConstructor.cpp
|
||||
Runtime/Temporal/InstantPrototype.cpp
|
||||
Runtime/Temporal/ISO8601.cpp
|
||||
Runtime/Temporal/Now.cpp
|
||||
Runtime/Temporal/PlainDate.cpp
|
||||
Runtime/Temporal/PlainDateConstructor.cpp
|
||||
Runtime/Temporal/PlainDatePrototype.cpp
|
||||
|
|
|
@ -282,6 +282,7 @@ namespace Temporal {
|
|||
JS_ENUMERATE_TEMPORAL_OBJECTS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
class Now;
|
||||
class Temporal;
|
||||
|
||||
struct CalendarDate;
|
||||
|
|
|
@ -538,6 +538,7 @@ namespace JS {
|
|||
P(timeLog) \
|
||||
P(timeStyle) \
|
||||
P(timeZone) \
|
||||
P(timeZoneId) \
|
||||
P(timeZoneName) \
|
||||
P(toArray) \
|
||||
P(toBase64) \
|
||||
|
|
132
Libraries/LibJS/Runtime/Temporal/Now.cpp
Normal file
132
Libraries/LibJS/Runtime/Temporal/Now.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/Now.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(Now);
|
||||
|
||||
// 2 The Temporal.Now Object, https://tc39.es/proposal-temporal/#sec-temporal-now-object
|
||||
Now::Now(Realm& realm)
|
||||
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void Now::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 2.1.1 Temporal.Now [ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal-now-%symbol.tostringtag%
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Now"_string), Attribute::Configurable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.timeZoneId, time_zone_id, 0, attr);
|
||||
define_native_function(realm, vm.names.instant, instant, 0, attr);
|
||||
define_native_function(realm, vm.names.plainDateTimeISO, plain_date_time_iso, 0, attr);
|
||||
define_native_function(realm, vm.names.plainDateISO, plain_date_iso, 0, attr);
|
||||
define_native_function(realm, vm.names.plainTimeISO, plain_time_iso, 0, attr);
|
||||
}
|
||||
|
||||
// 2.2.1 Temporal.Now.timeZoneId ( ), https://tc39.es/proposal-temporal/#sec-temporal.now.timezoneid
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::time_zone_id)
|
||||
{
|
||||
// 1. Return SystemTimeZoneIdentifier().
|
||||
return PrimitiveString::create(vm, system_time_zone_identifier());
|
||||
}
|
||||
|
||||
// 2.2.2 Temporal.Now.instant ( ), https://tc39.es/proposal-temporal/#sec-temporal.now.instant
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::instant)
|
||||
{
|
||||
// 1. Let ns be SystemUTCEpochNanoseconds().
|
||||
auto nanoseconds = system_utc_epoch_nanoseconds(vm);
|
||||
|
||||
// 2. Return ! CreateTemporalInstant(ns).
|
||||
return MUST(create_temporal_instant(vm, BigInt::create(vm, move(nanoseconds))));
|
||||
}
|
||||
|
||||
// 2.2.3 Temporal.Now.plainDateTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetimeiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_time_iso)
|
||||
{
|
||||
auto temporal_time_zone_like = vm.argument(0);
|
||||
|
||||
// 1. Let isoDateTime be ? SystemDateTime(temporalTimeZoneLike).
|
||||
auto iso_date_time = TRY(system_date_time(vm, temporal_time_zone_like));
|
||||
|
||||
// 2. Return ! CreateTemporalDateTime(isoDateTime, "iso8601").
|
||||
return MUST(create_temporal_date_time(vm, iso_date_time, "iso8601"_string));
|
||||
}
|
||||
|
||||
// 2.2.5 Temporal.Now.plainDateISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindateiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_iso)
|
||||
{
|
||||
auto temporal_time_zone_like = vm.argument(0);
|
||||
|
||||
// 1. Let isoDateTime be ? SystemDateTime(temporalTimeZoneLike).
|
||||
auto iso_date_time = TRY(system_date_time(vm, temporal_time_zone_like));
|
||||
|
||||
// 2. Return ! CreateTemporalDate(isoDateTime.[[ISODate]], "iso8601").
|
||||
return MUST(create_temporal_date(vm, iso_date_time.iso_date, "iso8601"_string));
|
||||
}
|
||||
|
||||
// 2.2.6 Temporal.Now.plainTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaintimeiso
|
||||
JS_DEFINE_NATIVE_FUNCTION(Now::plain_time_iso)
|
||||
{
|
||||
auto temporal_time_zone_like = vm.argument(0);
|
||||
|
||||
// 1. Let isoDateTime be ? SystemDateTime(temporalTimeZoneLike).
|
||||
auto iso_date_time = TRY(system_date_time(vm, temporal_time_zone_like));
|
||||
|
||||
// 2. Return ! CreateTemporalTime(isoDateTime.[[Time]]).
|
||||
return MUST(create_temporal_time(vm, iso_date_time.time));
|
||||
}
|
||||
|
||||
// 2.3.3 SystemUTCEpochNanoseconds ( ), https://tc39.es/proposal-temporal/#sec-temporal-systemutcepochnanoseconds
|
||||
Crypto::SignedBigInteger system_utc_epoch_nanoseconds(VM& vm)
|
||||
{
|
||||
// 1. Let global be GetGlobalObject().
|
||||
auto const& global = vm.get_global_object();
|
||||
|
||||
// 2. Let nowNs be HostSystemUTCEpochNanoseconds(global).
|
||||
auto now_ns = vm.host_system_utc_epoch_nanoseconds(global);
|
||||
|
||||
// 3. Return ℤ(nowNs).
|
||||
return now_ns;
|
||||
}
|
||||
|
||||
// 2.3.4 SystemDateTime ( temporalTimeZoneLike ), https://tc39.es/proposal-temporal/#sec-temporal-systemdatetime
|
||||
ThrowCompletionOr<ISODateTime> system_date_time(VM& vm, Value temporal_time_zone_like)
|
||||
{
|
||||
String time_zone;
|
||||
|
||||
// 1. If temporalTimeZoneLike is undefined, then
|
||||
if (temporal_time_zone_like.is_undefined()) {
|
||||
// a. Let timeZone be SystemTimeZoneIdentifier().
|
||||
time_zone = system_time_zone_identifier();
|
||||
}
|
||||
// 2. Else,
|
||||
else {
|
||||
// a. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike).
|
||||
time_zone = TRY(to_temporal_time_zone_identifier(vm, temporal_time_zone_like));
|
||||
}
|
||||
|
||||
// 3. Let epochNs be SystemUTCEpochNanoseconds().
|
||||
auto epoch_nanoseconds = system_utc_epoch_nanoseconds(vm);
|
||||
|
||||
// 4. Return GetISODateTimeFor(timeZone, epochNs).
|
||||
return get_iso_date_time_for(time_zone, epoch_nanoseconds);
|
||||
}
|
||||
|
||||
}
|
37
Libraries/LibJS/Runtime/Temporal/Now.h
Normal file
37
Libraries/LibJS/Runtime/Temporal/Now.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCrypto/BigInt/SignedBigInteger.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class Now final : public Object {
|
||||
JS_OBJECT(Now, Object);
|
||||
GC_DECLARE_ALLOCATOR(Now);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~Now() override = default;
|
||||
|
||||
private:
|
||||
explicit Now(Realm&);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(time_zone_id);
|
||||
JS_DECLARE_NATIVE_FUNCTION(instant);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_date_time_iso);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_date_iso);
|
||||
JS_DECLARE_NATIVE_FUNCTION(plain_time_iso);
|
||||
};
|
||||
|
||||
Crypto::SignedBigInteger system_utc_epoch_nanoseconds(VM&);
|
||||
ThrowCompletionOr<ISODateTime> system_date_time(VM&, Value temporal_time_zone_like);
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/Now.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
|
||||
|
@ -35,6 +36,7 @@ void Temporal::initialize(Realm& realm)
|
|||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal"_string), Attribute::Configurable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_direct_property(vm.names.Now, realm.create<Now>(realm), attr);
|
||||
define_intrinsic_accessor(vm.names.Duration, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_duration_constructor(); });
|
||||
define_intrinsic_accessor(vm.names.Instant, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_instant_constructor(); });
|
||||
define_intrinsic_accessor(vm.names.PlainDate, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_date_constructor(); });
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Time.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Bytecode/Interpreter.h>
|
||||
|
@ -29,6 +30,7 @@
|
|||
#include <LibJS/Runtime/PromiseCapability.h>
|
||||
#include <LibJS/Runtime/Reference.h>
|
||||
#include <LibJS/Runtime/Symbol.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibJS/SourceTextModule.h>
|
||||
#include <LibJS/SyntheticModule.h>
|
||||
|
@ -181,6 +183,20 @@ VM::VM(OwnPtr<CustomData> custom_data, ErrorMessages error_messages)
|
|||
return {};
|
||||
};
|
||||
|
||||
// 2.3.1 HostSystemUTCEpochNanoseconds ( global ), https://tc39.es/proposal-temporal/#sec-hostsystemutcepochnanoseconds
|
||||
host_system_utc_epoch_nanoseconds = [](Object const&) {
|
||||
// 1. Let ns be the approximate current UTC date and time, in nanoseconds since the epoch.
|
||||
Crypto::SignedBigInteger nanoseconds { AK::UnixDateTime::now().nanoseconds_since_epoch() };
|
||||
|
||||
// 2. Return the result of clamping ns between nsMinInstant and nsMaxInstant.
|
||||
if (nanoseconds < Temporal::NANOSECONDS_MIN_INSTANT)
|
||||
nanoseconds = Temporal::NANOSECONDS_MIN_INSTANT;
|
||||
if (nanoseconds > Temporal::NANOSECONDS_MAX_INSTANT)
|
||||
nanoseconds = Temporal::NANOSECONDS_MAX_INSTANT;
|
||||
|
||||
return nanoseconds;
|
||||
};
|
||||
|
||||
// AD-HOC: Inform the host that we received a date string we were unable to parse.
|
||||
host_unrecognized_date_string = [](StringView) {
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <AK/RefCounted.h>
|
||||
#include <AK/StackInfo.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibCrypto/Forward.h>
|
||||
#include <LibGC/Function.h>
|
||||
#include <LibGC/Heap.h>
|
||||
#include <LibGC/MarkedVector.h>
|
||||
|
@ -287,6 +288,7 @@ public:
|
|||
Function<ThrowCompletionOr<HandledByHost>(ArrayBuffer&, size_t)> host_resize_array_buffer;
|
||||
Function<void(StringView)> host_unrecognized_date_string;
|
||||
Function<ThrowCompletionOr<void>(Realm&, NonnullOwnPtr<ExecutionContext>, ShadowRealm&)> host_initialize_shadow_realm;
|
||||
Function<Crypto::SignedBigInteger(Object const& global)> host_system_utc_epoch_nanoseconds;
|
||||
|
||||
Vector<StackTraceElement> stack_trace() const;
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
test("basic functionality", () => {
|
||||
expect(Temporal.Now[Symbol.toStringTag]).toBe("Temporal.Now");
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.Now.plainDateISO).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainDate = Temporal.Now.plainDateISO();
|
||||
expect(plainDate).toBeInstanceOf(Temporal.PlainDate);
|
||||
expect(plainDate.calendarId).toBe("iso8601");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("invalid time zone name", () => {
|
||||
expect(() => {
|
||||
Temporal.Now.plainDateISO("foo");
|
||||
}).toThrowWithMessage(RangeError, "Invalid time zone name 'foo");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.Now.plainDateTimeISO).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainDateTime = Temporal.Now.plainDateTimeISO();
|
||||
expect(plainDateTime).toBeInstanceOf(Temporal.PlainDateTime);
|
||||
expect(plainDateTime.calendarId).toBe("iso8601");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("invalid time zone name", () => {
|
||||
expect(() => {
|
||||
Temporal.Now.plainDateTimeISO("foo");
|
||||
}).toThrowWithMessage(RangeError, "Invalid time zone name 'foo");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.Now.plainTimeISO).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainTime = Temporal.Now.plainTimeISO();
|
||||
expect(plainTime).toBeInstanceOf(Temporal.PlainTime);
|
||||
expect(plainTime.calendarId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("invalid time zone name", () => {
|
||||
expect(() => {
|
||||
Temporal.Now.plainTimeISO("foo");
|
||||
}).toThrowWithMessage(RangeError, "Invalid time zone name 'foo");
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue