From 092ec0cecf86d5a58469b8f60750c810e6a25df8 Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Thu, 9 Sep 2021 05:06:46 +0100 Subject: [PATCH] LibJS: Implement Temporal.PlainYearMonth.from --- .../Temporal/PlainYearMonthConstructor.cpp | 30 +++++++ .../Temporal/PlainYearMonthConstructor.h | 2 + .../PlainYearMonth/PlainYearMonth.from.js | 83 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainYearMonth/PlainYearMonth.from.js diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.cpp index 2900d46de91..d532c7887b9 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.cpp @@ -28,6 +28,9 @@ void PlainYearMonthConstructor::initialize(GlobalObject& global_object) define_direct_property(vm.names.prototype, global_object.temporal_plain_year_month_prototype(), 0); define_direct_property(vm.names.length, Value(2), Attribute::Configurable); + + u8 attr = Attribute::Writable | Attribute::Configurable; + define_native_function(vm.names.from, from, 1, attr); } // 9.1.1 Temporal.PlainYearMonth ( isoYear, isoMonth [ , calendarLike [ , referenceISODay ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth @@ -89,4 +92,31 @@ Value PlainYearMonthConstructor::construct(FunctionObject& new_target) return create_temporal_year_month(global_object, y, m, *calendar, ref, &new_target); } +// 9.2.2 Temporal.PlainYearMonth.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.from +JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthConstructor::from) +{ + // 1. Set options to ? GetOptionsObject(options). + auto* options = get_options_object(global_object, vm.argument(1)); + if (vm.exception()) + return {}; + + auto item = vm.argument(0); + + // 2. If Type(item) is Object and item has an [[InitializedTemporalYearMonth]] internal slot, then + if (item.is_object() && is(item.as_object())) { + // a. Perform ? ToTemporalOverflow(options). + (void)to_temporal_overflow(global_object, *options); + if (vm.exception()) + return {}; + + auto& plain_year_month_object = static_cast(item.as_object()); + + // b. Return ? CreateTemporalYearMonth(item.[[ISOYear]], item.[[ISOMonth]], item.[[Calendar]], item.[[ISODay]]). + return create_temporal_year_month(global_object, plain_year_month_object.iso_year(), plain_year_month_object.iso_month(), plain_year_month_object.calendar(), plain_year_month_object.iso_day()); + } + + // 3. Return ? ToTemporalYearMonth(item, options). + return to_temporal_year_month(global_object, item, options); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.h index 3669af06ced..16c13d07b62 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.h @@ -23,6 +23,8 @@ public: private: virtual bool has_constructor() const override { return true; } + + JS_DECLARE_NATIVE_FUNCTION(from); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainYearMonth/PlainYearMonth.from.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainYearMonth/PlainYearMonth.from.js new file mode 100644 index 00000000000..a47b44de369 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainYearMonth/PlainYearMonth.from.js @@ -0,0 +1,83 @@ +describe("correct behavior", () => { + test("length is 1", () => { + expect(Temporal.PlainYearMonth.from).toHaveLength(1); + }); + + test("PlainDate instance argument", () => { + const plainDate = new Temporal.PlainDate(2021, 7, 6); + const plainYearMonth = Temporal.PlainYearMonth.from(plainDate); + expect(plainYearMonth.year).toBe(2021); + expect(plainYearMonth.month).toBe(7); + expect(plainYearMonth.monthCode).toBe("M07"); + expect(plainYearMonth.daysInYear).toBe(365); + expect(plainYearMonth.daysInMonth).toBe(31); + expect(plainYearMonth.monthsInYear).toBe(12); + expect(plainYearMonth.inLeapYear).toBeFalse(); + }); + + test("PlainYearMonth instance argument", () => { + const plainYearMonth_ = new Temporal.PlainYearMonth(2021, 7); + const plainYearMonth = Temporal.PlainYearMonth.from(plainYearMonth_); + expect(plainYearMonth.year).toBe(2021); + expect(plainYearMonth.month).toBe(7); + expect(plainYearMonth.monthCode).toBe("M07"); + expect(plainYearMonth.daysInYear).toBe(365); + expect(plainYearMonth.daysInMonth).toBe(31); + expect(plainYearMonth.monthsInYear).toBe(12); + expect(plainYearMonth.inLeapYear).toBeFalse(); + }); + + test("ZonedDateTime instance argument", () => { + const timeZone = new Temporal.TimeZone("UTC"); + const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone); + const plainYearMonth = Temporal.PlainYearMonth.from(zonedDateTime); + expect(plainYearMonth.year).toBe(2021); + expect(plainYearMonth.month).toBe(7); + expect(plainYearMonth.monthCode).toBe("M07"); + expect(plainYearMonth.daysInYear).toBe(365); + expect(plainYearMonth.daysInMonth).toBe(31); + expect(plainYearMonth.monthsInYear).toBe(12); + expect(plainYearMonth.inLeapYear).toBeFalse(); + }); + + test("fields object argument", () => { + const object = { + year: 2021, + month: 7, + }; + const plainYearMonth = Temporal.PlainYearMonth.from(object); + expect(plainYearMonth.year).toBe(2021); + expect(plainYearMonth.month).toBe(7); + expect(plainYearMonth.monthCode).toBe("M07"); + expect(plainYearMonth.daysInYear).toBe(365); + expect(plainYearMonth.daysInMonth).toBe(31); + expect(plainYearMonth.monthsInYear).toBe(12); + expect(plainYearMonth.inLeapYear).toBeFalse(); + }); + + // Un-skip once ParseISODateTime & ParseTemporalYearMonthString are fully implemented + test.skip("PlainYearMonth string argument", () => { + const plainYearMonth = Temporal.PlainYearMonth.from("2021-07-06T23:42:01Z"); + expect(plainYearMonth.year).toBe(2021); + expect(plainYearMonth.month).toBe(7); + expect(plainYearMonth.monthCode).toBe("M07"); + expect(plainYearMonth.daysInYear).toBe(365); + expect(plainYearMonth.daysInMonth).toBe(31); + expect(plainYearMonth.monthsInYear).toBe(12); + expect(plainYearMonth.inLeapYear).toBeFalse(); + }); +}); + +describe("errors", () => { + test("missing fields", () => { + expect(() => { + Temporal.PlainYearMonth.from({}); + }).toThrowWithMessage(TypeError, "Required property year is missing or undefined"); + expect(() => { + Temporal.PlainYearMonth.from({ year: 0 }); + }).toThrowWithMessage(TypeError, "Required property month is missing or undefined"); + expect(() => { + Temporal.PlainYearMonth.from({ month: 1 }); + }).toThrowWithMessage(TypeError, "Required property year is missing or undefined"); + }); +});