diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h index 03a90af47d..e6b0ca34f5 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h @@ -245,7 +245,6 @@ M(TemporalInvalidPlainDateTime, "Invalid plain date time") \ M(TemporalInvalidPlainMonthDay, "Invalid plain month day") \ M(TemporalInvalidPlainTime, "Invalid plain time") \ - M(TemporalInvalidPlainTimeLikeObject, "Invalid plain time-like object") \ M(TemporalInvalidPlainYearMonth, "Invalid plain year month") \ M(TemporalInvalidTime, "Invalid time") \ M(TemporalInvalidTimeString, "Invalid time string '{}'") \ diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp index af6312ca6a..2c1ac87cce 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp @@ -336,43 +336,39 @@ ThrowCompletionOr create_temporal_time(GlobalObject& global_object, } // 4.5.8 ToTemporalTimeRecord ( temporalTimeLike [ , completeness ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimerecord -ThrowCompletionOr to_temporal_time_record(GlobalObject& global_object, Object const& temporal_time_like, ToTemporalTimeRecordCompleteness completeness) +ThrowCompletionOr to_temporal_time_record(GlobalObject& global_object, Object const& temporal_time_like, ToTemporalTimeRecordCompleteness completeness) { auto& vm = global_object.vm(); // 1. If completeness is not present, set completeness to complete. - // 2. Let result be the Record { [[Hour]]: undefined, [[Minute]]: undefined, [[Second]]: undefined, [[Millisecond]]: undefined, [[Microsecond]]: undefined, [[Nanosecond]]: undefined }. - auto result = UnregulatedTemporalTime {}; + // 2. Let partial be ? PrepareTemporalFields(temporalTimeLike, « "hour", "microsecond", "millisecond", "minute", "nanosecond", "second" », partial). + auto* partial = TRY(prepare_temporal_fields(global_object, temporal_time_like, { "hour"sv, "microsecond"sv, "millisecond"sv, "minute"sv, "nanosecond"sv, "second"sv }, PrepareTemporalFieldsPartial {})); - // 3. Let any be false. - auto any = false; + // 3. Let result be a new TemporalTimeLike Record with each field set to undefined. + auto result = TemporalTimeLikeRecord {}; - // 4. For each row of Table 3, except the header row, in table order, do - for (auto& [internal_slot, property] : temporal_time_like_properties>(vm)) { - // a. Let property be the Property value of the current row. + // 4. For each row of Table 4, except the header row, in table order, do + for (auto& [field, property_name] : temporal_time_like_record_fields>(vm)) { + // a. Let field be the Field Name value of the current row. + // b. Let propertyName be the Property Name value of the current row. - // b. Let value be ? Get(temporalTimeLike, property). - auto value = TRY(temporal_time_like.get(property)); + // c. Let valueDesc be OrdinaryGetOwnProperty(partial, propertyName). + auto value_descriptor = MUST(partial->Object::internal_get_own_property(property_name)); - // c. If value is not undefined, set any to true. - if (!value.is_undefined()) - any = true; + // d. If valueDesc is not undefined, then + if (value_descriptor.has_value()) { + // i. Assert: valueDesc is a data Property Descriptor. + VERIFY(value_descriptor->is_data_descriptor()); - // d. If value is not undefined or completeness is complete, then - if (!value.is_undefined() || completeness == ToTemporalTimeRecordCompleteness::Complete) { - // i. Set value to ? ToIntegerThrowOnInfinity(value). - auto value_number = TRY(to_integer_throw_on_infinity(global_object, value, ErrorType::TemporalPropertyMustBeFinite)); - - // ii. Set result's internal slot whose name is the Internal Slot value of the current row to value. - result.*internal_slot = value_number; + // ii. Set the field of result whose name is field to valueDesc.[[Value]]. + result.*field = value_descriptor->value->as_double(); + } + // e. Else if completeness is complete, then + else if (completeness == ToTemporalTimeRecordCompleteness::Complete) { + // i. Set the field of result whose name is field to 0. + result.*field = 0; } - } - - // 5. If any is false, then - if (!any) { - // a. Throw a TypeError exception. - return vm.throw_completion(global_object, ErrorType::TemporalInvalidPlainTimeLikeObject); } // 6. Return result. diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h index 5e027f302b..aab4d070e5 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h @@ -54,7 +54,7 @@ struct DaysAndTime { u16 nanosecond; }; -struct UnregulatedTemporalTime { +struct TemporalTimeLikeRecord { Optional hour; Optional minute; Optional second; @@ -63,24 +63,24 @@ struct UnregulatedTemporalTime { Optional nanosecond; }; -// Table 3: Properties of a TemporalTimeLike, https://tc39.es/proposal-temporal/#table-temporal-temporaltimelike-properties +// Table 4: TemporalTimeLike Record Fields, https://tc39.es/proposal-temporal/#table-temporal-temporaltimelike-properties template -struct TemporalTimeLikeProperty { - ValueT StructT::*internal_slot { nullptr }; - PropertyKey property; +struct TemporalTimeLikeRecordField { + ValueT StructT::*field_name { nullptr }; + PropertyKey property_name; }; template -auto temporal_time_like_properties = [](VM& vm) { - using PropertyT = TemporalTimeLikeProperty; +auto temporal_time_like_record_fields = [](VM& vm) { + using FieldT = TemporalTimeLikeRecordField; return AK::Array { - PropertyT { &StructT::hour, vm.names.hour }, - PropertyT { &StructT::microsecond, vm.names.microsecond }, - PropertyT { &StructT::millisecond, vm.names.millisecond }, - PropertyT { &StructT::minute, vm.names.minute }, - PropertyT { &StructT::nanosecond, vm.names.nanosecond }, - PropertyT { &StructT::second, vm.names.second }, + FieldT { &StructT::hour, vm.names.hour }, + FieldT { &StructT::microsecond, vm.names.microsecond }, + FieldT { &StructT::millisecond, vm.names.millisecond }, + FieldT { &StructT::minute, vm.names.minute }, + FieldT { &StructT::nanosecond, vm.names.nanosecond }, + FieldT { &StructT::second, vm.names.second }, }; }; @@ -96,7 +96,7 @@ bool is_valid_time(double hour, double minute, double second, double millisecond DaysAndTime balance_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond); TemporalTime constrain_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond); ThrowCompletionOr create_temporal_time(GlobalObject&, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, FunctionObject const* new_target = nullptr); -ThrowCompletionOr to_temporal_time_record(GlobalObject&, Object const& temporal_time_like, ToTemporalTimeRecordCompleteness = ToTemporalTimeRecordCompleteness::Complete); +ThrowCompletionOr to_temporal_time_record(GlobalObject&, Object const& temporal_time_like, ToTemporalTimeRecordCompleteness = ToTemporalTimeRecordCompleteness::Complete); String temporal_time_to_string(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant const& precision); i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2); DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.with.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.with.js index 51272816c4..8751e8650d 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.with.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainTime/PlainTime.prototype.with.js @@ -92,10 +92,16 @@ describe("errors", () => { test("argument is an invalid plain time-like object", () => { expect(() => { new Temporal.PlainTime().with({}); - }).toThrowWithMessage(TypeError, "Invalid plain time-like object"); + }).toThrowWithMessage( + TypeError, + "Object must have at least one of the following properties: hour, microsecond, millisecond, minute, nanosecond, second" + ); expect(() => { new Temporal.PlainTime().with({ foo: 1, bar: 2 }); - }).toThrowWithMessage(TypeError, "Invalid plain time-like object"); + }).toThrowWithMessage( + TypeError, + "Object must have at least one of the following properties: hour, microsecond, millisecond, minute, nanosecond, second" + ); }); test("error when coercing property to number", () => { diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withPlainTime.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withPlainTime.js index a8a40615fc..486e231d72 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withPlainTime.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.withPlainTime.js @@ -118,10 +118,16 @@ describe("errors", () => { test("invalid plain time-like object", () => { expect(() => { new Temporal.ZonedDateTime(1n, {}).withPlainTime({}); - }).toThrowWithMessage(TypeError, "Invalid plain time-like object"); + }).toThrowWithMessage( + TypeError, + "Object must have at least one of the following properties: hour, microsecond, millisecond, minute, nanosecond, second" + ); expect(() => { new Temporal.ZonedDateTime(1n, {}).withPlainTime({ foo: 1, bar: 2 }); - }).toThrowWithMessage(TypeError, "Invalid plain time-like object"); + }).toThrowWithMessage( + TypeError, + "Object must have at least one of the following properties: hour, microsecond, millisecond, minute, nanosecond, second" + ); }); });