LibJS: Start implementing Temporal.ZonedDateTime

This commit adds the ZonedDateTime object itself, its constructor and
prototype (currently empty), and the CreateTemporalZonedDateTime
abstract operation.
This commit is contained in:
Linus Groh 2021-08-01 17:20:11 +01:00
parent 1b9b995f93
commit cfb77b66e5
Notes: sideshowbarker 2024-07-18 07:38:24 +09:00
11 changed files with 297 additions and 1 deletions

View File

@ -148,6 +148,9 @@ set(SOURCES
Runtime/Temporal/TimeZone.cpp
Runtime/Temporal/TimeZoneConstructor.cpp
Runtime/Temporal/TimeZonePrototype.cpp
Runtime/Temporal/ZonedDateTime.cpp
Runtime/Temporal/ZonedDateTimeConstructor.cpp
Runtime/Temporal/ZonedDateTimePrototype.cpp
Runtime/TypedArray.cpp
Runtime/TypedArrayConstructor.cpp
Runtime/TypedArrayPrototype.cpp

View File

@ -83,7 +83,8 @@
__JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
__JS_ENUMERATE(PlainDateTime, plain_date_time, PlainDateTimePrototype, PlainDateTimeConstructor) \
__JS_ENUMERATE(PlainTime, plain_time, PlainTimePrototype, PlainTimeConstructor) \
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor)
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor) \
__JS_ENUMERATE(ZonedDateTime, zoned_date_time, ZonedDateTimePrototype, ZonedDateTimeConstructor)
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
__JS_ENUMERATE(Iterator, iterator) \

View File

@ -84,6 +84,8 @@
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/TypedArrayConstructor.h>
#include <LibJS/Runtime/TypedArrayPrototype.h>

View File

@ -14,6 +14,7 @@
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
namespace JS::Temporal {
@ -41,6 +42,7 @@ void Temporal::initialize(GlobalObject& global_object)
define_direct_property(vm.names.PlainDateTime, global_object.temporal_plain_date_time_constructor(), attr);
define_direct_property(vm.names.PlainTime, global_object.temporal_plain_time_constructor(), attr);
define_direct_property(vm.names.TimeZone, global_object.temporal_time_zone_constructor(), attr);
define_direct_property(vm.names.ZonedDateTime, global_object.temporal_zoned_date_time_constructor(), attr);
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
namespace JS::Temporal {
// 6 Temporal.ZonedDateTime Objects, https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-objects
ZonedDateTime::ZonedDateTime(BigInt& nanoseconds, Object& time_zone, Object& calendar, Object& prototype)
: Object(prototype)
, m_nanoseconds(nanoseconds)
, m_time_zone(time_zone)
, m_calendar(calendar)
{
}
void ZonedDateTime::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(&m_nanoseconds);
visitor.visit(&m_time_zone);
visitor.visit(&m_calendar);
}
// 6.5.3 CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalzoneddatetime
ZonedDateTime* create_temporal_zoned_date_time(GlobalObject& global_object, BigInt& epoch_nanoseconds, Object& time_zone, Object& calendar, FunctionObject* new_target)
{
auto& vm = global_object.vm();
// 1. Assert: Type(epochNanoseconds) is BigInt.
// 3. Assert: Type(timeZone) is Object.
// 4. Assert: Type(calendar) is Object.
// 2. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.
VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds));
// 5. If newTarget is not present, set it to %Temporal.ZonedDateTime%.
if (!new_target)
new_target = global_object.temporal_zoned_date_time_constructor();
// 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.ZonedDateTime.prototype%", « [[InitializedTemporalZonedDateTime]], [[Nanoseconds]], [[TimeZone]], [[Calendar]] »).
// 7. Set object.[[Nanoseconds]] to epochNanoseconds.
// 8. Set object.[[TimeZone]] to timeZone.
// 9. Set object.[[Calendar]] to calendar.
auto* object = ordinary_create_from_constructor<ZonedDateTime>(global_object, *new_target, &GlobalObject::temporal_time_zone_prototype, epoch_nanoseconds, time_zone, calendar);
if (vm.exception())
return {};
// 10. Return object.
return object;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
class ZonedDateTime final : public Object {
JS_OBJECT(ZonedDateTime, Object);
public:
ZonedDateTime(BigInt& nanoseconds, Object& time_zone, Object& calendar, Object& prototype);
virtual ~ZonedDateTime() override = default;
BigInt const& nanoseconds() const { return m_nanoseconds; }
BigInt& nanoseconds() { return m_nanoseconds; }
Object const& time_zone() const { return m_time_zone; }
Object& time_zone() { return m_time_zone; }
Object const& calendar() const { return m_calendar; }
Object& calendar() { return m_calendar; }
private:
virtual void visit_edges(Visitor&) override;
// 6.4 Properties of Temporal.ZonedDateTime Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-zoneddatetime-instances
BigInt& m_nanoseconds; // [[Nanoseconds]]
Object& m_time_zone; // [[TimeZone]]
Object& m_calendar; // [[Calendar]]
};
ZonedDateTime* create_temporal_zoned_date_time(GlobalObject&, BigInt& epoch_nanoseconds, Object& time_zone, Object& calendar, FunctionObject* new_target = nullptr);
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
namespace JS::Temporal {
// 6.1 The Temporal.ZonedDateTime Constructor, https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-constructor
ZonedDateTimeConstructor::ZonedDateTimeConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.ZonedDateTime.as_string(), *global_object.function_prototype())
{
}
void ZonedDateTimeConstructor::initialize(GlobalObject& global_object)
{
NativeFunction::initialize(global_object);
auto& vm = this->vm();
// 6.2.1 Temporal.ZonedDateTime.prototype, https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-prototype
define_direct_property(vm.names.prototype, global_object.temporal_zoned_date_time_prototype(), 0);
define_direct_property(vm.names.length, Value(2), Attribute::Configurable);
}
// 6.1.1 Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime
Value ZonedDateTimeConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, then
// a. Throw a TypeError exception.
vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, "Temporal.ZonedDateTime");
return {};
}
// 6.1.1 Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime
Value ZonedDateTimeConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
auto& global_object = this->global_object();
// 2. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).
auto* epoch_nanoseconds = vm.argument(0).to_bigint(global_object);
if (vm.exception())
return {};
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(*epoch_nanoseconds)) {
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidEpochNanoseconds);
return {};
}
// 4. Let timeZone be ? ToTemporalTimeZone(timeZoneLike).
auto* time_zone = to_temporal_time_zone(global_object, vm.argument(1));
if (vm.exception())
return {};
// 5. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
auto* calendar = to_temporal_calendar_with_iso_default(global_object, vm.argument(2));
if (vm.exception())
return {};
// 6. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar, NewTarget).
return create_temporal_zoned_date_time(global_object, *epoch_nanoseconds, *time_zone, *calendar, &new_target);
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class ZonedDateTimeConstructor final : public NativeFunction {
JS_OBJECT(ZonedDateTimeConstructor, NativeFunction);
public:
explicit ZonedDateTimeConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ZonedDateTimeConstructor() override = default;
virtual Value call() override;
virtual Value construct(FunctionObject& new_target) override;
private:
virtual bool has_constructor() const override { return true; }
};
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h>
namespace JS::Temporal {
// 6.3 Properties of the Temporal.ZonedDateTime Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-zoneddatetime-prototype-object
ZonedDateTimePrototype::ZonedDateTimePrototype(GlobalObject& global_object)
: Object(*global_object.object_prototype())
{
}
void ZonedDateTimePrototype::initialize(GlobalObject& global_object)
{
Object::initialize(global_object);
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
class ZonedDateTimePrototype final : public Object {
JS_OBJECT(ZonedDateTimePrototype, Object);
public:
explicit ZonedDateTimePrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~ZonedDateTimePrototype() override = default;
};
}

View File

@ -0,0 +1,39 @@
describe("errors", () => {
test("called without new", () => {
expect(() => {
Temporal.ZonedDateTime();
}).toThrowWithMessage(
TypeError,
"Temporal.ZonedDateTime constructor must be called with 'new'"
);
});
test("out-of-range epoch nanoseconds value", () => {
expect(() => {
new Temporal.ZonedDateTime(8_640_000_000_000_000_000_001n);
}).toThrowWithMessage(
RangeError,
"Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17"
);
expect(() => {
new Temporal.ZonedDateTime(-8_640_000_000_000_000_000_001n);
}).toThrowWithMessage(
RangeError,
"Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17"
);
});
});
describe("normal behavior", () => {
test("length is 2", () => {
expect(Temporal.ZonedDateTime).toHaveLength(2);
});
test("basic functionality", () => {
const timeZone = new Temporal.TimeZone("UTC");
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
expect(typeof zonedDateTime).toBe("object");
expect(zonedDateTime).toBeInstanceOf(Temporal.ZonedDateTime);
expect(Object.getPrototypeOf(zonedDateTime)).toBe(Temporal.ZonedDateTime.prototype);
});
});