summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2021-07-14 21:01:12 +0100
committerLinus Groh <mail@linusgroh.de>2021-07-14 23:50:03 +0100
commita2f1d79765970c5ddf18914a4cc6913673b1f1b7 (patch)
tree1df3f3b09b939c299206f983e9eaade772586c9b
parent48b66c7a68000559f4b3657d499efd9460ed8740 (diff)
downloadserenity-a2f1d79765970c5ddf18914a4cc6913673b1f1b7.zip
LibJS: Start implementing Temporal.Calendar
Just like the previous Temporal.{Instant,TimeZone} commits, this patch adds the Calendar object itself, its constructor and prototype (currently empty), and two required abstract operations.
-rw-r--r--Userland/Libraries/LibJS/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibJS/Forward.h5
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorTypes.h3
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.cpp2
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp44
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h20
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.cpp64
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.h28
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp23
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.h22
-rw-r--r--Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp2
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Temporal/Calendar/Calendar.js38
12 files changed, 250 insertions, 3 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt
index 921ce02bac..a289b85195 100644
--- a/Userland/Libraries/LibJS/CMakeLists.txt
+++ b/Userland/Libraries/LibJS/CMakeLists.txt
@@ -123,6 +123,8 @@ set(SOURCES
Runtime/SymbolPrototype.cpp
Runtime/Temporal/AbstractOperations.cpp
Runtime/Temporal/Calendar.cpp
+ Runtime/Temporal/CalendarConstructor.cpp
+ Runtime/Temporal/CalendarPrototype.cpp
Runtime/Temporal/Instant.cpp
Runtime/Temporal/InstantConstructor.cpp
Runtime/Temporal/InstantPrototype.cpp
diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h
index 7df5cbec22..5032199e38 100644
--- a/Userland/Libraries/LibJS/Forward.h
+++ b/Userland/Libraries/LibJS/Forward.h
@@ -76,8 +76,9 @@
__JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \
__JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double)
-#define JS_ENUMERATE_TEMPORAL_OBJECTS \
- __JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
+#define JS_ENUMERATE_TEMPORAL_OBJECTS \
+ __JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \
+ __JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor)
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
index ee685a5093..6220ae4f41 100644
--- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
+++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
@@ -166,8 +166,9 @@
M(StringNonGlobalRegExp, "RegExp argument is non-global") \
M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \
M(StringRepeatCountMustBe, "repeat count must be a {} number") \
- M(TemporalInvalidISODate, "Invalid ISO date") \
+ M(TemporalInvalidCalendarIdentifier, "Invalid calendar identifier '{}'") \
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") \
M(TemporalInvalidTimeZoneName, "Invalid time zone name") \
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
index 2b729d4deb..a56cff9e54 100644
--- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
+++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
@@ -68,6 +68,8 @@
#include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolPrototype.h>
+#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
+#include <LibJS/Runtime/Temporal/CalendarPrototype.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp
index f9948eea02..aa53db94bb 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp
@@ -4,11 +4,55 @@
* 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/CalendarConstructor.h>
#include <LibJS/Runtime/Value.h>
namespace JS::Temporal {
+// 12 Temporal.Calendar Objects, https://tc39.es/proposal-temporal/#sec-temporal-calendar-objects
+Calendar::Calendar(String identifier, Object& prototype)
+ : Object(prototype)
+ , m_identifier(move(identifier))
+{
+}
+
+// 12.1.1 CreateTemporalCalendar ( identifier [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalcalendar
+Calendar* create_temporal_calendar(GlobalObject& global_object, String const& identifier, FunctionObject* new_target)
+{
+ auto& vm = global_object.vm();
+
+ // 1. Assert: ! IsBuiltinCalendar(identifier) is true.
+ VERIFY(is_builtin_calendar(identifier));
+
+ // 2. If newTarget is not provided, set newTarget to %Temporal.Calendar%.
+ if (!new_target)
+ new_target = global_object.temporal_calendar_constructor();
+
+ // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Calendar.prototype%", ยซ [[InitializedTemporalCalendar]], [[Identifier]] ยป).
+ // 4. Set object.[[Identifier]] to identifier.
+ auto* object = ordinary_create_from_constructor<Calendar>(global_object, *new_target, &GlobalObject::temporal_calendar_prototype, identifier);
+ if (vm.exception())
+ return {};
+
+ // 5. Return object.
+ return object;
+}
+
+// 12.1.2 IsBuiltinCalendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal-isbuiltincalendar
+// NOTE: This is the minimum IsBuiltinCalendar implementation for engines without ECMA-402.
+bool is_builtin_calendar(String const& identifier)
+{
+ // 1. If id is not "iso8601", return false.
+ if (identifier != "iso8601"sv)
+ return false;
+
+ // 2. Return true.
+ return true;
+}
+
// 12.1.30 IsISOLeapYear ( year ), https://tc39.es/proposal-temporal/#sec-temporal-isisoleapyear
bool is_iso_leap_year(i32 year)
{
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h
index edb742c566..e7b8ba1839 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h
@@ -1,15 +1,35 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
+ * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
+#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Value.h>
namespace JS::Temporal {
+class Calendar final : public Object {
+ JS_OBJECT(Calendar, Object);
+
+public:
+ explicit Calendar(String identifier, Object& prototype);
+ virtual ~Calendar() override = default;
+
+ String const& identifier() const { return m_identifier; }
+
+private:
+ // 12.5 Properties of Temporal.Calendar Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-calendar-instances
+
+ // [[Identifier]]
+ String m_identifier;
+};
+
+Calendar* create_temporal_calendar(GlobalObject&, String const& identifier, FunctionObject* new_target = nullptr);
+bool is_builtin_calendar(String const& identifier);
bool is_iso_leap_year(i32 year);
i32 iso_days_in_month(i32 year, i32 month);
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.cpp
new file mode 100644
index 0000000000..c4b6b5863f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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/CalendarConstructor.h>
+
+namespace JS::Temporal {
+
+// 12.2 The Temporal.Calendar Constructor, https://tc39.es/proposal-temporal/#sec-temporal-calendar-constructor
+CalendarConstructor::CalendarConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Calendar.as_string(), *global_object.function_prototype())
+{
+}
+
+void CalendarConstructor::initialize(GlobalObject& global_object)
+{
+ NativeFunction::initialize(global_object);
+
+ auto& vm = this->vm();
+
+ // 12.3.1 Temporal.Calendar.prototype, https://tc39.es/proposal-temporal/#sec-temporal-calendar-prototype
+ define_direct_property(vm.names.prototype, global_object.temporal_calendar_prototype(), 0);
+
+ define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
+}
+
+// 12.2.1 Temporal.Calendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal.calendar
+Value CalendarConstructor::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.Calendar");
+ return {};
+}
+
+// 12.2.1 Temporal.Calendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal.calendar
+Value CalendarConstructor::construct(FunctionObject& new_target)
+{
+ auto& vm = this->vm();
+ auto& global_object = this->global_object();
+
+ // 2. Set id to ? ToString(id).
+ auto identifier = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ // 3. If ! IsBuiltinCalendar(id) is false, then
+ if (!is_builtin_calendar(identifier)) {
+ // a. Throw a RangeError exception.
+ vm.throw_exception<RangeError>(global_object, ErrorType::TemporalInvalidCalendarIdentifier, identifier);
+ return {};
+ }
+
+ // 4. Return ? CreateTemporalCalendar(id, NewTarget).
+ return create_temporal_calendar(global_object, identifier, &new_target);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.h b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.h
new file mode 100644
index 0000000000..5ce91aaa72
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarConstructor.h
@@ -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 CalendarConstructor final : public NativeFunction {
+ JS_OBJECT(CalendarConstructor, NativeFunction);
+
+public:
+ explicit CalendarConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~CalendarConstructor() override = default;
+
+ virtual Value call() override;
+ virtual Value construct(FunctionObject& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp
new file mode 100644
index 0000000000..d362658085
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.cpp
@@ -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/CalendarPrototype.h>
+
+namespace JS::Temporal {
+
+// 12.4 Properties of the Temporal.Calendar Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-calendar-prototype-object
+CalendarPrototype::CalendarPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void CalendarPrototype::initialize(GlobalObject& global_object)
+{
+ Object::initialize(global_object);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.h b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.h
new file mode 100644
index 0000000000..4f17d1b68f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/CalendarPrototype.h
@@ -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 CalendarPrototype final : public Object {
+ JS_OBJECT(CalendarPrototype, Object);
+
+public:
+ explicit CalendarPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~CalendarPrototype() override = default;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp
index b35583c7ab..83c208fb71 100644
--- a/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/Temporal.cpp
@@ -5,6 +5,7 @@
*/
#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/Now.h>
#include <LibJS/Runtime/Temporal/Temporal.h>
@@ -26,6 +27,7 @@ void Temporal::initialize(GlobalObject& global_object)
u8 attr = Attribute::Writable | Attribute::Configurable;
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.Instant, global_object.temporal_instant_constructor(), attr);
define_direct_property(vm.names.TimeZone, global_object.temporal_time_zone_constructor(), attr);
}
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Calendar/Calendar.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Calendar/Calendar.js
new file mode 100644
index 0000000000..4ddefcf3d7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Calendar/Calendar.js
@@ -0,0 +1,38 @@
+describe("errors", () => {
+ test("called without new", () => {
+ expect(() => {
+ Temporal.Calendar();
+ }).toThrowWithMessage(TypeError, "Temporal.Calendar constructor must be called with 'new'");
+ });
+
+ test("argument must be coercible to string", () => {
+ expect(() => {
+ new Temporal.Calendar({
+ toString() {
+ throw new Error();
+ },
+ });
+ }).toThrow(Error);
+ });
+
+ test("invalid calendar identifier", () => {
+ expect(() => {
+ new Temporal.Calendar("foo");
+ }).toThrowWithMessage(RangeError, "Invalid calendar identifier 'foo'");
+ });
+});
+
+describe("normal behavior", () => {
+ test("length is 1", () => {
+ expect(Temporal.Calendar).toHaveLength(1);
+ });
+
+ test("basic functionality", () => {
+ const calendar = new Temporal.Calendar("iso8601");
+ // FIXME: Enable this once Temporal.Calendar.prototype.id is implemented
+ // expect(calendar.id).toBe("iso8601");
+ expect(typeof calendar).toBe("object");
+ expect(calendar).toBeInstanceOf(Temporal.Calendar);
+ expect(Object.getPrototypeOf(calendar)).toBe(Temporal.Calendar.prototype);
+ });
+});