mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +00:00
LibJS: Start implementing Temporal.Duration
This patch adds the Duration object itself, its constructor and prototype (currently empty), and three required abstract operations.
This commit is contained in:
parent
71234b3716
commit
7921d8ba91
Notes:
sideshowbarker
2024-07-18 08:58:16 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/7921d8ba916 Pull-request: https://github.com/SerenityOS/serenity/pull/8794 Reviewed-by: https://github.com/IdanHo ✅
13 changed files with 377 additions and 1 deletions
|
@ -125,6 +125,9 @@ set(SOURCES
|
|||
Runtime/Temporal/Calendar.cpp
|
||||
Runtime/Temporal/CalendarConstructor.cpp
|
||||
Runtime/Temporal/CalendarPrototype.cpp
|
||||
Runtime/Temporal/Duration.cpp
|
||||
Runtime/Temporal/DurationConstructor.cpp
|
||||
Runtime/Temporal/DurationPrototype.cpp
|
||||
Runtime/Temporal/Instant.cpp
|
||||
Runtime/Temporal/InstantConstructor.cpp
|
||||
Runtime/Temporal/InstantPrototype.cpp
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
|
||||
#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(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor)
|
||||
|
||||
|
|
|
@ -167,6 +167,7 @@
|
|||
M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \
|
||||
M(StringRepeatCountMustBe, "repeat count must be a {} number") \
|
||||
M(TemporalInvalidCalendarIdentifier, "Invalid calendar identifier '{}'") \
|
||||
M(TemporalInvalidDuration, "Invalid duration") \
|
||||
M(TemporalInvalidEpochNanoseconds, "Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17") \
|
||||
M(TemporalInvalidISODate, "Invalid ISO date") \
|
||||
M(TemporalInvalidTime, "Invalid time") \
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
#include <LibJS/Runtime/SymbolPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
|
|
107
Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp
Normal file
107
Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
// 7 Temporal.Duration Objects, https://tc39.es/proposal-temporal/#sec-temporal-duration-objects
|
||||
Duration::Duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object& prototype)
|
||||
: Object(prototype)
|
||||
, m_years(years)
|
||||
, m_months(months)
|
||||
, m_weeks(weeks)
|
||||
, m_days(days)
|
||||
, m_hours(hours)
|
||||
, m_minutes(minutes)
|
||||
, m_seconds(seconds)
|
||||
, m_milliseconds(milliseconds)
|
||||
, m_microseconds(microseconds)
|
||||
, m_nanoseconds(nanoseconds)
|
||||
{
|
||||
}
|
||||
|
||||
// 7.5.3 DurationSign ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )
|
||||
i8 duration_sign(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
|
||||
{
|
||||
// 1. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
|
||||
for (auto& v : { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }) {
|
||||
// a. If v < 0, return −1.
|
||||
if (v < 0)
|
||||
return -1;
|
||||
|
||||
// b. If v > 0, return 1.
|
||||
if (v > 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 2. Return 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 7.5.4 IsValidDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds )
|
||||
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
|
||||
{
|
||||
// 1. Let sign be ! DurationSign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
|
||||
auto sign = duration_sign(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
|
||||
|
||||
// 2. For each value v of « years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds », do
|
||||
for (auto& v : { years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds }) {
|
||||
// a. If v is not finite, return false.
|
||||
if (!isfinite(v))
|
||||
return false;
|
||||
|
||||
// b. If v < 0 and sign > 0, return false.
|
||||
if (v < 0 && sign > 0)
|
||||
return false;
|
||||
|
||||
// c. If v > 0 and sign < 0, return false.
|
||||
if (v > 0 && sign < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 7.5.7 CreateTemporalDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalduration
|
||||
Duration* create_temporal_duration(GlobalObject& global_object, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, FunctionObject* new_target)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
|
||||
// 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds)) {
|
||||
vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidDuration);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 2. If newTarget is not present, set it to %Temporal.Duration%.
|
||||
if (!new_target)
|
||||
new_target = global_object.temporal_duration_constructor();
|
||||
|
||||
// 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Duration.prototype%", « [[InitializedTemporalDuration]], [[Years]], [[Months]], [[Weeks]], [[Days]], [[Hours]], [[Minutes]], [[Seconds]], [[Milliseconds]], [[Microseconds]], [[Nanoseconds]] »).
|
||||
// 4. Set object.[[Years]] to years.
|
||||
// 5. Set object.[[Months]] to months.
|
||||
// 6. Set object.[[Weeks]] to weeks.
|
||||
// 7. Set object.[[Days]] to days.
|
||||
// 8. Set object.[[Hours]] to hours.
|
||||
// 9. Set object.[[Minutes]] to minutes.
|
||||
// 10. Set object.[[Seconds]] to seconds.
|
||||
// 11. Set object.[[Milliseconds]] to milliseconds.
|
||||
// 12. Set object.[[Microseconds]] to microseconds.
|
||||
// 13. Set object.[[Nanoseconds]] to nanoseconds.
|
||||
auto* object = ordinary_create_from_constructor<Duration>(global_object, *new_target, &GlobalObject::temporal_duration_prototype, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 14. Return object.
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
50
Userland/Libraries/LibJS/Runtime/Temporal/Duration.h
Normal file
50
Userland/Libraries/LibJS/Runtime/Temporal/Duration.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 Duration final : public Object {
|
||||
JS_OBJECT(Duration, Object);
|
||||
|
||||
public:
|
||||
explicit Duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object& prototype);
|
||||
virtual ~Duration() override = default;
|
||||
|
||||
double years() const { return m_years; }
|
||||
double months() const { return m_months; }
|
||||
double weeks() const { return m_weeks; }
|
||||
double days() const { return m_days; }
|
||||
double hours() const { return m_hours; }
|
||||
double minutes() const { return m_minutes; }
|
||||
double seconds() const { return m_seconds; }
|
||||
double milliseconds() const { return m_milliseconds; }
|
||||
double microseconds() const { return m_microseconds; }
|
||||
double nanoseconds() const { return m_nanoseconds; }
|
||||
|
||||
private:
|
||||
// 7.4 Properties of Temporal.Duration Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-duration-instances
|
||||
|
||||
double m_years; // [[Years]]
|
||||
double m_months; // [[Months]]
|
||||
double m_weeks; // [[Weeks]]
|
||||
double m_days; // [[Days]]
|
||||
double m_hours; // [[Hours]]
|
||||
double m_minutes; // [[Minutes]]
|
||||
double m_seconds; // [[Seconds]]
|
||||
double m_milliseconds; // [[Milliseconds]]
|
||||
double m_microseconds; // [[Microseconds]]
|
||||
double m_nanoseconds; // [[Nanoseconds]]
|
||||
};
|
||||
|
||||
i8 duration_sign(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||
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);
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
// 7.1 The Temporal.Duration Constructor, https://tc39.es/proposal-temporal/#sec-temporal-duration-constructor
|
||||
DurationConstructor::DurationConstructor(GlobalObject& global_object)
|
||||
: NativeFunction(vm().names.Duration.as_string(), *global_object.function_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void DurationConstructor::initialize(GlobalObject& global_object)
|
||||
{
|
||||
NativeFunction::initialize(global_object);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 7.2.1 Temporal.Duration.prototype, https://tc39.es/proposal-temporal/#sec-temporal-duration-prototype
|
||||
define_direct_property(vm.names.prototype, global_object.temporal_duration_prototype(), 0);
|
||||
|
||||
define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
|
||||
}
|
||||
|
||||
// 7.1.1 Temporal.Duration ( [ years [ , months [ , weeks [ , days [ , hours [ , minutes [ , seconds [ , milliseconds [ , microseconds [ , nanoseconds ] ] ] ] ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration
|
||||
Value DurationConstructor::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.Duration");
|
||||
return {};
|
||||
}
|
||||
|
||||
// 7.1.1 Temporal.Duration ( [ years [ , months [ , weeks [ , days [ , hours [ , minutes [ , seconds [ , milliseconds [ , microseconds [ , nanoseconds ] ] ] ] ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration
|
||||
Value DurationConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
auto& global_object = this->global_object();
|
||||
|
||||
// 2. Let y be ? ToIntegerOrInfinity(years).
|
||||
auto y = vm.argument(0).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 3. Let mo be ? ToIntegerOrInfinity(months).
|
||||
auto mo = vm.argument(1).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 4. Let w be ? ToIntegerOrInfinity(weeks).
|
||||
auto w = vm.argument(2).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 5. Let d be ? ToIntegerOrInfinity(days).
|
||||
auto d = vm.argument(3).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 6. Let h be ? ToIntegerOrInfinity(hours).
|
||||
auto h = vm.argument(4).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 7. Let m be ? ToIntegerOrInfinity(minutes).
|
||||
auto m = vm.argument(5).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 8. Let s be ? ToIntegerOrInfinity(seconds).
|
||||
auto s = vm.argument(6).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 9. Let ms be ? ToIntegerOrInfinity(milliseconds).
|
||||
auto ms = vm.argument(7).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 10. Let mis be ? ToIntegerOrInfinity(microseconds).
|
||||
auto mis = vm.argument(8).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 11. Let ns be ? ToIntegerOrInfinity(nanoseconds).
|
||||
auto ns = vm.argument(9).to_integer_or_infinity(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
// 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget).
|
||||
return create_temporal_duration(global_object, y, mo, w, d, h, m, s, ms, mis, ns, &new_target);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 DurationConstructor final : public NativeFunction {
|
||||
JS_OBJECT(DurationConstructor, NativeFunction);
|
||||
|
||||
public:
|
||||
explicit DurationConstructor(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~DurationConstructor() 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, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
// 7.3 Properties of the Temporal.Duration Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-duration-prototype-object
|
||||
DurationPrototype::DurationPrototype(GlobalObject& global_object)
|
||||
: Object(*global_object.object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void DurationPrototype::initialize(GlobalObject& global_object)
|
||||
{
|
||||
Object::initialize(global_object);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 DurationPrototype final : public Object {
|
||||
JS_OBJECT(DurationPrototype, Object);
|
||||
|
||||
public:
|
||||
explicit DurationPrototype(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~DurationPrototype() override = default;
|
||||
};
|
||||
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/Now.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
|
@ -28,6 +29,7 @@ void Temporal::initialize(GlobalObject& global_object)
|
|||
|
||||
define_direct_property(vm.names.now, heap().allocate<Now>(global_object, global_object), attr);
|
||||
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.TimeZone, global_object.temporal_time_zone_constructor(), attr);
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@ namespace JS::Temporal {
|
|||
class TimeZone final : public Object {
|
||||
JS_OBJECT(TimeZone, Object);
|
||||
|
||||
public:
|
||||
// Needs to store values in the range -8.64 * 10^13 to 8.64 * 10^13
|
||||
using OffsetType = double;
|
||||
|
||||
public:
|
||||
explicit TimeZone(String identifier, Object& prototype);
|
||||
virtual ~TimeZone() override = default;
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
describe("errors", () => {
|
||||
test("called without new", () => {
|
||||
expect(() => {
|
||||
Temporal.Duration();
|
||||
}).toThrowWithMessage(TypeError, "Temporal.Duration constructor must be called with 'new'");
|
||||
});
|
||||
|
||||
test("cannot mix arguments with different signs", () => {
|
||||
expect(() => {
|
||||
new Temporal.Duration(-1, 1);
|
||||
}).toThrowWithMessage(RangeError, "Invalid duration");
|
||||
expect(() => {
|
||||
new Temporal.Duration(1, -1);
|
||||
}).toThrowWithMessage(RangeError, "Invalid duration");
|
||||
});
|
||||
|
||||
test("cannot pass Infinity", () => {
|
||||
expect(() => {
|
||||
new Temporal.Duration(Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid duration");
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
test("length is 0", () => {
|
||||
expect(Temporal.Duration).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const duration = new Temporal.Duration();
|
||||
expect(typeof duration).toBe("object");
|
||||
expect(duration).toBeInstanceOf(Temporal.Duration);
|
||||
expect(Object.getPrototypeOf(duration)).toBe(Temporal.Duration.prototype);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue