mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-30 08:41:15 +00:00
LibJS: Start implementing Temporal.PlainDate
This commit adds the PlainDate object itself, its constructor and prototype (currently empty), and several required abstract operations.
This commit is contained in:
parent
ff6ca0f02d
commit
cc00ccec41
Notes:
sideshowbarker
2024-07-18 08:46:00 +09:00
Author: https://github.com/IdanHo Commit: https://github.com/SerenityOS/serenity/commit/cc00ccec411 Pull-request: https://github.com/SerenityOS/serenity/pull/8870 Reviewed-by: https://github.com/linusg
|
@ -135,6 +135,8 @@ set(SOURCES
|
|||
Runtime/Temporal/InstantPrototype.cpp
|
||||
Runtime/Temporal/Now.cpp
|
||||
Runtime/Temporal/PlainDate.cpp
|
||||
Runtime/Temporal/PlainDateConstructor.cpp
|
||||
Runtime/Temporal/PlainDatePrototype.cpp
|
||||
Runtime/Temporal/PlainDateTime.cpp
|
||||
Runtime/Temporal/PlainTime.cpp
|
||||
Runtime/Temporal/Temporal.cpp
|
||||
|
|
|
@ -76,10 +76,11 @@
|
|||
__JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \
|
||||
__JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double)
|
||||
|
||||
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
||||
__JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \
|
||||
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
||||
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
|
||||
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
||||
__JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \
|
||||
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
||||
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
|
||||
__JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
|
||||
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor)
|
||||
|
||||
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
|
||||
|
|
|
@ -78,6 +78,7 @@ namespace JS {
|
|||
P(buffer) \
|
||||
P(byteLength) \
|
||||
P(byteOffset) \
|
||||
P(calendar) \
|
||||
P(call) \
|
||||
P(callee) \
|
||||
P(caller) \
|
||||
|
|
|
@ -172,6 +172,7 @@
|
|||
M(TemporalInvalidDurationPropertyValue, "Invalid value for duration property '{}': must be an integer, got {}") \
|
||||
M(TemporalInvalidEpochNanoseconds, "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17") \
|
||||
M(TemporalInvalidISODate, "Invalid ISO date") \
|
||||
M(TemporalInvalidPlainDate, "Invalid plain date") \
|
||||
M(TemporalInvalidTime, "Invalid time") \
|
||||
M(TemporalInvalidTimeZoneName, "Invalid time zone name") \
|
||||
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
|
||||
|
|
|
@ -75,6 +75,8 @@
|
|||
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDatePrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>
|
||||
|
|
|
@ -423,6 +423,27 @@ Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject& global_obj
|
|||
return TemporalInstant { .year = result->year, .month = result->month, .day = result->day, .hour = result->hour, .minute = result->minute, .second = result->second, .millisecond = result->millisecond, .microsecond = result->microsecond, .nanosecond = result->nanosecond, .time_zone_offset = move(time_zone_result->offset) };
|
||||
}
|
||||
|
||||
// 13.37 ParseTemporalCalendarString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalcalendarstring
|
||||
String parse_temporal_calendar_string([[maybe_unused]] GlobalObject& global_object, [[maybe_unused]] String const& iso_string)
|
||||
{
|
||||
// 1. Assert: Type(isoString) is String.
|
||||
|
||||
// 2. If isoString does not satisfy the syntax of a TemporalCalendarString (see 13.33), then
|
||||
// a. Throw a RangeError exception.
|
||||
// 3. Let id be the part of isoString produced by the CalendarName production, or undefined if not present.
|
||||
Optional<StringView> id_part;
|
||||
TODO();
|
||||
|
||||
// 4. If id is undefined, then
|
||||
if (!id_part.has_value()) {
|
||||
// a. Return "iso8601".
|
||||
return "iso8601";
|
||||
}
|
||||
|
||||
// 5. Return id.
|
||||
return id_part.value();
|
||||
}
|
||||
|
||||
// 13.40 ParseTemporalDurationString ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporaldurationstring
|
||||
Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject& global_object, String const& iso_string)
|
||||
{
|
||||
|
|
|
@ -60,6 +60,7 @@ Optional<String> to_smallest_temporal_unit(GlobalObject&, Object& normalized_opt
|
|||
BigInt* round_number_to_increment(GlobalObject&, BigInt const&, u64 increment, String const& rounding_mode);
|
||||
Optional<ISODateTime> parse_iso_date_time(GlobalObject&, String const& iso_string);
|
||||
Optional<TemporalInstant> parse_temporal_instant_string(GlobalObject&, String const& iso_string);
|
||||
String parse_temporal_calendar_string(GlobalObject&, String const& iso_string);
|
||||
Optional<TemporalDuration> parse_temporal_duration_string(GlobalObject&, String const& iso_string);
|
||||
Optional<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject&, String const& iso_string);
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
@ -53,6 +55,98 @@ bool is_builtin_calendar(String const& identifier)
|
|||
return true;
|
||||
}
|
||||
|
||||
// 12.1.3 GetBuiltinCalendar ( id )
|
||||
Calendar* get_builtin_calendar(GlobalObject& global_object, String const& identifier)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
|
||||
// 1. If ! IsBuiltinCalendar(id) is false, throw a RangeError exception.
|
||||
if (!is_builtin_calendar(identifier)) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidCalendarIdentifier, identifier);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 2. Return ? Construct(%Temporal.Calendar%, « id »).
|
||||
MarkedValueList arguments(vm.heap());
|
||||
arguments.append(js_string(vm, identifier));
|
||||
auto calendar = vm.construct(*global_object.temporal_calendar_constructor(), *global_object.temporal_calendar_constructor(), move(arguments));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
return static_cast<Calendar*>(&calendar.as_object());
|
||||
}
|
||||
|
||||
// 12.1.4 GetISO8601Calendar ( )
|
||||
Calendar* get_iso8601_calendar(GlobalObject& global_object)
|
||||
{
|
||||
// 1. Return ? GetBuiltinCalendar("iso8601").
|
||||
return get_builtin_calendar(global_object, "iso8601");
|
||||
}
|
||||
|
||||
// 12.1.21 ToTemporalCalendar ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendar
|
||||
Object* to_temporal_calendar(GlobalObject& global_object, Value temporal_calendar_like)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
|
||||
// 1. If Type(temporalCalendarLike) is Object, then
|
||||
if (temporal_calendar_like.is_object()) {
|
||||
auto& temporal_calendar_like_object = temporal_calendar_like.as_object();
|
||||
// a. If temporalCalendarLike has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
// TODO: The rest of the Temporal built-ins
|
||||
if (is<PlainDate>(temporal_calendar_like_object)) {
|
||||
// i. Return temporalCalendarLike.[[Calendar]].
|
||||
return &static_cast<PlainDate&>(temporal_calendar_like_object).calendar();
|
||||
}
|
||||
|
||||
// b. If ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
|
||||
auto has_property = temporal_calendar_like_object.has_property(vm.names.calendar);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (!has_property)
|
||||
return &temporal_calendar_like_object;
|
||||
|
||||
// c. Set temporalCalendarLike to ? Get(temporalCalendarLike, "calendar").
|
||||
temporal_calendar_like = temporal_calendar_like_object.get(vm.names.calendar);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
// d. If Type(temporalCalendarLike) is Object and ? HasProperty(temporalCalendarLike, "calendar") is false, return temporalCalendarLike.
|
||||
if (temporal_calendar_like.is_object()) {
|
||||
has_property = temporal_calendar_like.as_object().has_property(vm.names.calendar);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (!has_property)
|
||||
return &temporal_calendar_like.as_object();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Let identifier be ? ToString(temporalCalendarLike).
|
||||
auto identifier = temporal_calendar_like.to_string(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 3. If ! IsBuiltinCalendar(identifier) is false, then
|
||||
if (!is_builtin_calendar(identifier)) {
|
||||
// a. Let identifier be ? ParseTemporalCalendarString(identifier).
|
||||
identifier = parse_temporal_calendar_string(global_object, identifier);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
}
|
||||
|
||||
// 4. Return ? CreateTemporalCalendar(identifier).
|
||||
return create_temporal_calendar(global_object, identifier);
|
||||
}
|
||||
|
||||
// 12.1.22 ToTemporalCalendarWithISODefault ( temporalCalendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalcalendarwithisodefault
|
||||
Object* to_temporal_calendar_with_iso_default(GlobalObject& global_object, Value temporal_calendar_like)
|
||||
{
|
||||
// 1. If temporalCalendarLike is undefined, then
|
||||
if (temporal_calendar_like.is_undefined()) {
|
||||
// a. Return ? GetISO8601Calendar().
|
||||
return get_iso8601_calendar(global_object);
|
||||
}
|
||||
// 2. Return ? ToTemporalCalendar(temporalCalendarLike).
|
||||
return to_temporal_calendar(global_object, temporal_calendar_like);
|
||||
}
|
||||
|
||||
// 12.1.30 IsISOLeapYear ( year ), https://tc39.es/proposal-temporal/#sec-temporal-isisoleapyear
|
||||
bool is_iso_leap_year(i32 year)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,10 @@ private:
|
|||
|
||||
Calendar* create_temporal_calendar(GlobalObject&, String const& identifier, FunctionObject* new_target = nullptr);
|
||||
bool is_builtin_calendar(String const& identifier);
|
||||
Calendar* get_builtin_calendar(GlobalObject&, String const& identifier);
|
||||
Calendar* get_iso8601_calendar(GlobalObject&);
|
||||
Object* to_temporal_calendar(GlobalObject&, Value);
|
||||
Object* to_temporal_calendar_with_iso_default(GlobalObject&, Value);
|
||||
bool is_iso_leap_year(i32 year);
|
||||
i32 iso_days_in_month(i32 year, i32 month);
|
||||
|
||||
|
|
|
@ -4,12 +4,68 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
// 3 Temporal.PlainDate Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-objects
|
||||
PlainDate::PlainDate(i32 year, i32 month, i32 day, Object& calendar, Object& prototype)
|
||||
: Object(prototype)
|
||||
, m_iso_year(year)
|
||||
, m_iso_month(month)
|
||||
, m_iso_day(day)
|
||||
, m_calendar(calendar)
|
||||
{
|
||||
}
|
||||
|
||||
void PlainDate::visit_edges(Visitor& visitor)
|
||||
{
|
||||
visitor.visit(&m_calendar);
|
||||
}
|
||||
|
||||
// 3.5.1 CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldate
|
||||
PlainDate* create_temporal_date(GlobalObject& global_object, i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, FunctionObject* new_target)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
|
||||
// 1. Assert: isoYear is an integer.
|
||||
// 2. Assert: isoMonth is an integer.
|
||||
// 3. Assert: isoDay is an integer.
|
||||
// 4. Assert: Type(calendar) is Object.
|
||||
|
||||
// 5. If ! IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
|
||||
if (!is_valid_iso_date(iso_year, iso_month, iso_day)) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 6. If ! ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
|
||||
if (!iso_date_time_within_limits(global_object, iso_year, iso_month, iso_day, 12, 0, 0, 0, 0, 0)) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 7. If newTarget is not present, set it to %Temporal.PlainDate%.
|
||||
if (!new_target)
|
||||
new_target = global_object.temporal_plain_date_constructor();
|
||||
|
||||
// 8. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDate.prototype%", « [[InitializedTemporalDate]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).
|
||||
// 9. Set object.[[ISOYear]] to isoYear.
|
||||
// 10. Set object.[[ISOMonth]] to isoMonth.
|
||||
// 11. Set object.[[ISODay]] to isoDay.
|
||||
// 12. Set object.[[Calendar]] to calendar.
|
||||
auto* object = ordinary_create_from_constructor<PlainDate>(global_object, *new_target, &GlobalObject::temporal_plain_date_prototype, iso_year, iso_month, iso_day, calendar);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
// 3.5.5 IsValidISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidisodate
|
||||
bool is_valid_iso_date(i32 year, i32 month, i32 day)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,30 @@
|
|||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainDate final : public Object {
|
||||
JS_OBJECT(PlainDate, Object);
|
||||
|
||||
public:
|
||||
explicit PlainDate(i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, Object& prototype);
|
||||
virtual ~PlainDate() override = default;
|
||||
|
||||
[[nodiscard]] i32 iso_year() const { return m_iso_year; }
|
||||
[[nodiscard]] i32 iso_month() const { return m_iso_month; }
|
||||
[[nodiscard]] i32 iso_day() const { return m_iso_day; }
|
||||
[[nodiscard]] Object const& calendar() const { return m_calendar; }
|
||||
[[nodiscard]] Object& calendar() { return m_calendar; }
|
||||
|
||||
private:
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
// 3.4 Properties of Temporal.PlainDate Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plaindate-instances
|
||||
i32 m_iso_year { 0 }; // [[ISOYear]]
|
||||
i32 m_iso_month { 1 }; // [[ISOMonth]]
|
||||
i32 m_iso_day { 1 }; // [[ISODay]]
|
||||
Object& m_calendar; // [[Calendar]]
|
||||
};
|
||||
|
||||
PlainDate* create_temporal_date(GlobalObject&, i32 iso_year, i32 iso_month, i32 iso_day, Object& calendar, FunctionObject* new_target);
|
||||
bool is_valid_iso_date(i32 year, i32 month, i32 day);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
// 3.1 The Temporal.PlainDate Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-constructor
|
||||
PlainDateConstructor::PlainDateConstructor(GlobalObject& global_object)
|
||||
: NativeFunction(vm().names.PlainDate.as_string(), *global_object.function_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void PlainDateConstructor::initialize(GlobalObject& global_object)
|
||||
{
|
||||
NativeFunction::initialize(global_object);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 3.2.1 Temporal.PlainDate.prototype, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-prototype
|
||||
define_direct_property(vm.names.prototype, global_object.temporal_plain_date_prototype(), 0);
|
||||
|
||||
define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
|
||||
}
|
||||
|
||||
// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
|
||||
Value PlainDateConstructor::call()
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. If NewTarget is undefined, throw a TypeError exception.
|
||||
vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, "Temporal.PlainDate");
|
||||
return {};
|
||||
}
|
||||
|
||||
// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
|
||||
Value PlainDateConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
auto& global_object = this->global_object();
|
||||
|
||||
// 2. Let y be ? ToIntegerOrInfinity(isoYear).
|
||||
auto y = vm.argument(0).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
// 3. If y is +∞ or -∞, throw a RangeError exception.
|
||||
if (Value(y).is_infinity()) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 4. Let m be ? ToIntegerOrInfinity(isoMonth).
|
||||
auto m = vm.argument(1).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
// 5. If m is +∞ or -∞, throw a RangeError exception.
|
||||
if (Value(m).is_infinity()) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 6. Let d be ? ToIntegerOrInfinity(isoDay).
|
||||
auto d = vm.argument(2).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
// 7. If d is +∞ or -∞, throw a RangeError exception.
|
||||
if (Value(d).is_infinity()) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 8. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
|
||||
auto* calendar = to_temporal_calendar_with_iso_default(global_object, vm.argument(3));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
|
||||
// This does not change the exposed behaviour as the call to CreateTemporalDate will immediately check that these values are valid
|
||||
// ISO values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
|
||||
if (!AK::is_within_range<i32>(y) || !AK::is_within_range<i32>(m) || !AK::is_within_range<i32>(d)) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidPlainDate);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 9. Return ? CreateTemporalDate(y, m, d, calendar, NewTarget).
|
||||
return create_temporal_date(global_object, y, m, d, *calendar, &new_target);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainDateConstructor final : public NativeFunction {
|
||||
JS_OBJECT(PlainDateConstructor, NativeFunction);
|
||||
|
||||
public:
|
||||
explicit PlainDateConstructor(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~PlainDateConstructor() override = default;
|
||||
|
||||
virtual Value call() override;
|
||||
virtual Value construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
virtual bool has_constructor() const override { return true; }
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDatePrototype.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
// 3.3 Properties of the Temporal.PlainDate Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plaindate-prototype-object
|
||||
PlainDatePrototype::PlainDatePrototype(GlobalObject& global_object)
|
||||
: Object(*global_object.object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void PlainDatePrototype::initialize(GlobalObject& global_object)
|
||||
{
|
||||
Object::initialize(global_object);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainDatePrototype final : public Object {
|
||||
JS_OBJECT(PlainDatePrototype, Object);
|
||||
|
||||
public:
|
||||
explicit PlainDatePrototype(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~PlainDatePrototype() override = default;
|
||||
};
|
||||
|
||||
}
|
|
@ -41,4 +41,32 @@ BigInt* get_epoch_from_iso_parts(GlobalObject& global_object, i32 year, i32 mont
|
|||
return js_bigint(vm.heap(), Crypto::SignedBigInteger::create_from(static_cast<i64>(ms.as_double())).multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }).plus(Crypto::SignedBigInteger::create_from((i64)microsecond * 1000)).plus(Crypto::SignedBigInteger(nanosecond)));
|
||||
}
|
||||
|
||||
// -864 * 10^19 - 864 * 10^14
|
||||
const auto DATETIME_NANOSECONDS_MIN = "-8640086400000000000000"_sbigint;
|
||||
// +864 * 10^19 + 864 * 10^14
|
||||
const auto DATETIME_NANOSECONDS_MAX = "8640086400000000000000"_sbigint;
|
||||
|
||||
// 5.5.2 ISODateTimeWithinLimits ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-isodatetimewithinlimits
|
||||
bool iso_date_time_within_limits(GlobalObject& global_object, i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 millisecond, i32 microsecond, i32 nanosecond)
|
||||
{
|
||||
// 1. Assert: year, month, day, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
|
||||
|
||||
// 2. Let ns be ! GetEpochFromISOParts(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond).
|
||||
auto ns = get_epoch_from_iso_parts(global_object, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
|
||||
|
||||
// 3. If ns ≤ -8.64 × 10^21 - 8.64 × 10^16, then
|
||||
if (ns->big_integer() <= DATETIME_NANOSECONDS_MIN) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. If ns ≥ 8.64 × 10^21 + 8.64 × 10^16, then
|
||||
if (ns->big_integer() >= DATETIME_NANOSECONDS_MAX) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
// 5. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,5 +11,6 @@
|
|||
namespace JS::Temporal {
|
||||
|
||||
BigInt* get_epoch_from_iso_parts(GlobalObject&, i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 millisecond, i32 microsecond, i32 nanosecond);
|
||||
bool iso_date_time_within_limits(GlobalObject&, i32 year, i32 month, i32 day, i32 hour, i32 minute, i32 second, i32 millisecond, i32 microsecond, i32 nanosecond);
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#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/Temporal.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
|
||||
|
||||
|
@ -31,6 +32,7 @@ void Temporal::initialize(GlobalObject& global_object)
|
|||
define_direct_property(vm.names.Calendar, global_object.temporal_calendar_constructor(), attr);
|
||||
define_direct_property(vm.names.Duration, global_object.temporal_duration_constructor(), attr);
|
||||
define_direct_property(vm.names.Instant, global_object.temporal_instant_constructor(), attr);
|
||||
define_direct_property(vm.names.PlainDate, global_object.temporal_plain_date_constructor(), attr);
|
||||
define_direct_property(vm.names.TimeZone, global_object.temporal_time_zone_constructor(), attr);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
describe("errors", () => {
|
||||
test("called without new", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainDate();
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
"Temporal.PlainDate constructor must be called with 'new'"
|
||||
);
|
||||
});
|
||||
|
||||
test("cannot pass Infinity", () => {
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(0, Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(0, 0, Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(-Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(0, -Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(0, 0, -Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
});
|
||||
|
||||
test("cannot pass invalid ISO month/day", () => {
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(0, 0, 1);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
expect(() => {
|
||||
new Temporal.PlainDate(0, 1, 0);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain date");
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.PlainDate).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainDate = new Temporal.PlainDate(2021, 7, 19);
|
||||
expect(typeof plainDate).toBe("object");
|
||||
expect(plainDate).toBeInstanceOf(Temporal.PlainDate);
|
||||
expect(Object.getPrototypeOf(plainDate)).toBe(Temporal.PlainDate.prototype);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue