From 3f75cf270ad827c0c685e5e4f6fe4ed9ecf45228 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sun, 20 Jul 2025 12:52:53 -0400 Subject: [PATCH] LibJS: Move ambiguous Temporal time string handling to a separate parser This is an editorial change in the Temporal proposal. See: https://github.com/tc39/proposal-temporal/commit/fa3d0b9 --- Libraries/LibJS/Runtime/Temporal/ISO8601.cpp | 21 ++++++++++--------- Libraries/LibJS/Runtime/Temporal/ISO8601.h | 3 ++- .../LibJS/Runtime/Temporal/PlainTime.cpp | 13 ++++++------ .../Temporal/PlainTime/PlainTime.from.js | 2 +- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp b/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp index 946f4f7acc9..6606efa0c14 100644 --- a/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp +++ b/Libraries/LibJS/Runtime/Temporal/ISO8601.cpp @@ -150,6 +150,15 @@ public: return parse_annotated_date_time(Zoned::No, TimeRequired::Yes) || parse_annotated_time(); } + // https://tc39.es/proposal-temporal/#prod-AmbiguousTemporalTimeString + [[nodiscard]] bool parse_ambiguous_temporal_time_string() + { + // AmbiguousTemporalTimeString ::: + // DateSpecMonthDay TimeZoneAnnotation[opt] Annotations[opt] + // DateSpecYearMonth TimeZoneAnnotation[opt] Annotations[opt] + return parse_annotated_month_day() || parse_annotated_year_month(); + } + // https://tc39.es/proposal-temporal/#prod-TemporalYearMonthString [[nodiscard]] bool parse_temporal_year_month_string() { @@ -199,16 +208,7 @@ public: // AnnotatedTime ::: // TimeDesignator Time DateTimeUTCOffset[~Z][opt] TimeZoneAnnotation[opt] Annotations[opt] // Time DateTimeUTCOffset[~Z][opt] TimeZoneAnnotation[opt] Annotations[opt] - auto has_time_designator = parse_time_designator(); - - if (!has_time_designator) { - StateTransaction transaction { *this }; - - // It is a Syntax Error if ParseText(Time DateTimeUTCOffset[~Z], DateSpecMonthDay) is a Parse Node. - // It is a Syntax Error if ParseText(Time DateTimeUTCOffset[~Z], DateSpecYearMonth) is a Parse Node. - if (parse_date_spec_month_day() || parse_date_spec_year_month()) - return false; - } + (void)parse_time_designator(); if (!parse_time()) return false; @@ -1303,6 +1303,7 @@ private: }; #define JS_ENUMERATE_ISO8601_PRODUCTION_PARSERS \ + __JS_ENUMERATE(AmbiguousTemporalTimeString, parse_ambiguous_temporal_time_string) \ __JS_ENUMERATE(AnnotationValue, parse_annotation_value) \ __JS_ENUMERATE(DateMonth, parse_date_month) \ __JS_ENUMERATE(TemporalDateTimeString, parse_temporal_date_time_string) \ diff --git a/Libraries/LibJS/Runtime/Temporal/ISO8601.h b/Libraries/LibJS/Runtime/Temporal/ISO8601.h index 298732868e9..6e350e91433 100644 --- a/Libraries/LibJS/Runtime/Temporal/ISO8601.h +++ b/Libraries/LibJS/Runtime/Temporal/ISO8601.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2021-2022, Linus Groh - * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -60,6 +60,7 @@ struct ParseResult { }; enum class Production { + AmbiguousTemporalTimeString, AnnotationValue, DateMonth, TemporalDateTimeString, diff --git a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp index ea6c35e3ab9..1430c15de65 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2021, Idan Horowitz * Copyright (c) 2021-2023, Linus Groh - * Copyright (c) 2024, Tim Flynn + * Copyright (c) 2024-2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ @@ -169,15 +169,16 @@ ThrowCompletionOr> to_temporal_time(VM& vm, Value item, Value // b. Let parseResult be ? ParseISODateTime(item, « TemporalTimeString »). auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalTimeString } })); - // c. Assert: parseResult.[[Time]] is not START-OF-DAY. + // c. If ParseText(StringToCodePoints(item), AmbiguousTemporalTimeString) is a Parse Node, throw a RangeError exception. + if (parse_iso8601(Production::AmbiguousTemporalTimeString, item.as_string().utf8_string_view()).has_value()) + return vm.throw_completion(ErrorType::TemporalInvalidPlainTime); + + // d. Assert: parseResult.[[Time]] is not START-OF-DAY. VERIFY(!parse_result.time.has()); - // d. Set result to parseResult.[[Time]]. + // e. Set result to parseResult.[[Time]]. time = parse_result.time.get